What I ran into
A request the Python server rejects at payload validation answers with the FastAPI default body, a flat detail array, not the UCP error envelope:
POST /checkout-sessions
{"line_items": "not-an-array"}
HTTP 422 content-type: application/json
{"detail": [{"type": "list_type", "loc": ["body", "line_items"],
"msg": "Input should be a valid list", "input": "not-an-array"}]}
Expected
checkout-rest.md shapes protocol errors as a JSON body carrying code and content. The server already answers the errors it raises itself in that shape through the UcpError handler, so a platform sees two rejection grammars from the same server depending on which layer rejects.
Root cause
server.py registers exactly one exception handler, for UcpError. RequestValidationError has no handler, so FastAPI renders its default shape and the platform gets a body with no code member to act on.
Where it comes from
Same ecosystem pitfall as the Node lane, fixed there in #192: the framework default speaks before the protocol does on the validation path. A RequestValidationError handler that renders the existing envelope, keeping the pydantic loc and msg detail as content, closes it.
Why CI did not catch it
The integration tests assert status codes on invalid bodies, not body shape, so the default shape passes. The suite is green on this commit, 222 passed locally.
What I ran into
A request the Python server rejects at payload validation answers with the FastAPI default body, a flat detail array, not the UCP error envelope:
Expected
checkout-rest.md shapes protocol errors as a JSON body carrying code and content. The server already answers the errors it raises itself in that shape through the UcpError handler, so a platform sees two rejection grammars from the same server depending on which layer rejects.
Root cause
server.py registers exactly one exception handler, for UcpError. RequestValidationError has no handler, so FastAPI renders its default shape and the platform gets a body with no code member to act on.
Where it comes from
Same ecosystem pitfall as the Node lane, fixed there in #192: the framework default speaks before the protocol does on the validation path. A RequestValidationError handler that renders the existing envelope, keeping the pydantic loc and msg detail as content, closes it.
Why CI did not catch it
The integration tests assert status codes on invalid bodies, not body shape, so the default shape passes. The suite is green on this commit, 222 passed locally.