Skip to content

fix(server): answer a rejected request body the way every other error is answered - #21

Closed
glatinone wants to merge 1 commit into
docs/front-page-and-demo-giffrom
fix/validation-error-envelope
Closed

glatinone wants to merge 1 commit into
docs/front-page-and-demo-giffrom
fix/validation-error-envelope

Conversation

@glatinone

Copy link
Copy Markdown
Owner

fix(server): answer a rejected request body the way every other error is answered

FastAPI raises RequestValidationError before a route runs, so it never reached the
AMPError handler: a body the schema rejects answered {"detail": [...]} while
every other error answered {"error": {"code", "message", "details"}}. The API
reference documented 422 VALIDATION_ERROR all along, and both SDKs read
error.code - so a caller that sent a bad field got a generic "HTTP error 422" and
no idea which field was wrong. That is the exact failure the single error shape was
introduced to remove in the first place; it just had a hole where the framework
replies before the application does.

  • One handler for RequestValidationError, returning the protocol envelope with
    code: VALIDATION_ERROR. The field errors move into error.details.errors,
    which is what details is for.
  • jsonable_encoder on those errors, so a rejected value that is not JSON
    serialisable (a bytes body, say) cannot turn this handler into a 500 - the error
    path must not be the thing that breaks.
  • The contract is corrected too. FastAPI documents its own validation body on
    every route that can reject input, so the committed openapi.json told client
    generators to expect HTTPValidationError while the server sent something else.
    document_the_error_envelope rewrites each 422 to ErrorResponse and drops the
    now-unreferenced HTTPValidationError/ValidationError components, because a
    contract is what somebody generates a client from.
  • The conformance suite gained a vector, so any implementation is held to the same
    shape rather than only this one (39 vectors now, 39/39 against a live server).

The first version of the schema cleanup deleted every component, including
ErrorResponse, because it searched for references inside the components block
instead of the whole document - most refs live in the paths. The openapi contract
test caught it on the first run, which is the argument for that test existing.

… is answered

FastAPI raises `RequestValidationError` before a route runs, so it never reached the
`AMPError` handler: a body the schema rejects answered `{"detail": [...]}` while
every other error answered `{"error": {"code", "message", "details"}}`. The API
reference documented `422 VALIDATION_ERROR` all along, and both SDKs read
`error.code` - so a caller that sent a bad field got a generic "HTTP error 422" and
no idea which field was wrong. That is the exact failure the single error shape was
introduced to remove in the first place; it just had a hole where the framework
replies before the application does.

- One handler for `RequestValidationError`, returning the protocol envelope with
  `code: VALIDATION_ERROR`. The field errors move into `error.details.errors`,
  which is what `details` is for.
- `jsonable_encoder` on those errors, so a rejected value that is not JSON
  serialisable (a bytes body, say) cannot turn this handler into a 500 - the error
  path must not be the thing that breaks.
- **The contract is corrected too.** FastAPI documents its own validation body on
  every route that can reject input, so the committed `openapi.json` told client
  generators to expect `HTTPValidationError` while the server sent something else.
  `document_the_error_envelope` rewrites each 422 to `ErrorResponse` and drops the
  now-unreferenced `HTTPValidationError`/`ValidationError` components, because a
  contract is what somebody generates a client from.
- The conformance suite gained a vector, so any implementation is held to the same
  shape rather than only this one (39 vectors now, 39/39 against a live server).

The first version of the schema cleanup deleted *every* component, including
`ErrorResponse`, because it searched for references inside the components block
instead of the whole document - most refs live in the paths. The openapi contract
test caught it on the first run, which is the argument for that test existing.
@glatinone

Copy link
Copy Markdown
Owner Author

Landed on master in the v0.1.0 chain: the branch was fast-forward merged as part of b940905..92b88ee and released as v0.1.0. Closing so the open list matches reality - the commits are in master, and the tag points at them.

@glatinone glatinone closed this Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant