Skip to main content

FastAPI Validation Override

Build Status Package version Supported Python versions License: MIT

FastAPI returns 422 Unprocessable Entity for every request validation failure. Many APIs, client teams, and HTTP standards treat 400 Bad Request as the correct status code for malformed input. Fixing this in FastAPI requires wiring a custom exception handler and updating the OpenAPI schema separately. override_validation_error does both in a single call.

Features

  • Single call: patches runtime exception handling and the OpenAPI schema at once
  • Any status code: use 400, 409, or any valid code instead of 422
  • anyOf merge: when a route already declares a response at the target code, the validation error schema is merged rather than overwritten
  • Custom openapi preserved: wraps any app.openapi function already installed and applies the patch on top of its output
  • Bring your own handler: handle_exceptions=False skips the built-in handler while still patching the schema
  • Idempotent: safe to call multiple times on the same app instance
  • No-op guard: status_code=422 leaves FastAPI behavior unchanged

Requirements

  • Python 3.10+
  • FastAPI 0.120.0+

Installation

pip install fastapi-router-versioning
# or
uv add fastapi-router-versioning

Quick start

from fastapi import FastAPI
from pydantic import BaseModel

from fastapi_validation_override import override_validation_error

app = FastAPI()


class Item(BaseModel):
    name: str
    price: float


@app.post("/items")
async def create_item(item: Item) -> dict[str, object]:
    return item.model_dump()


override_validation_error(app)
# POST /items with invalid fields -> 400 Bad Request {"detail": [...]}

The {"detail": [...]} body is identical to FastAPI's default 422 response. Only the status code changes.

To use a different code, pass status_code:

override_validation_error(app, status_code=409)

Reference

override_validation_error

override_validation_error(app, status_code=400, handle_exceptions=True)
Parameter Type Default Description
app FastAPI required The FastAPI application instance to patch
status_code int 400 HTTP status code to use instead of 422. Calling with 422 is a no-op
handle_exceptions bool True When True, registers an exception handler that returns the custom status code at runtime. Set to False to patch only the OpenAPI schema and handle the exception yourself

Custom exception handler

Set handle_exceptions=False when you need a custom response body or additional logic. The OpenAPI schema is still patched.

from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

from fastapi_validation_override import override_validation_error

app = FastAPI()


@app.exception_handler(RequestValidationError)
async def validation_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
    return JSONResponse(
        status_code=400,
        content={"message": "Validation failed", "errors": exc.errors()},
    )


@app.post("/items")
async def create_item(item: Item) -> dict[str, object]:
    return item.model_dump()


override_validation_error(app, status_code=400, handle_exceptions=False)

Preserving a custom app.openapi

Call override_validation_error after assigning your custom openapi function. The library captures app.openapi at call time and wraps it, so the order matters.

from typing import Any

from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi

from fastapi_validation_override import override_validation_error

app = FastAPI()


def custom_openapi() -> dict[str, Any]:
    if app.openapi_schema:
        return app.openapi_schema
    schema = get_openapi(title="My API", version="1.0.0", routes=app.routes)
    schema["info"]["x-logo"] = {"url": "https://example.com/logo.png"}
    app.openapi_schema = schema
    return schema


app.openapi = custom_openapi  # type: ignore[method-assign]
override_validation_error(app)  # must come after

Merging with an existing response at the target code

When a route already declares a response at the target status code, override_validation_error merges the schemas using anyOf instead of overwriting the existing one.

class OutOfStockError(BaseModel):
    message: str
    item_name: str


@app.post("/items", responses={400: {"model": OutOfStockError, "description": "Out of stock"}})
async def create_item(item: Item) -> dict[str, object]:
    return item.model_dump()


override_validation_error(app)
# schema at 400: anyOf: [OutOfStockError, HTTPValidationError]

Examples

Runnable examples are in the examples/ directory:

File Description
basic.py Minimal setup with the default 400 status code
custom_status_code.py Using a custom status code (409)
handle_exceptions_false.py Custom exception handler with schema-only patch
custom_openapi.py Preserving a custom app.openapi function
existing_response_at_target_code.py anyOf merge when the target code is already declared
with_apirouter.py Usage with multiple APIRouter instances

Release Notes

RELEASE_NOTES

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastapi_validation_override-0.1.1.tar.gz (76.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_validation_override-0.1.1-py3-none-any.whl (6.3 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_validation_override-0.1.1.tar.gz.

File metadata

  • Download URL: fastapi_validation_override-0.1.1.tar.gz
  • Upload date:
  • Size: 76.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.18 {"installer":{"name":"uv","version":"0.11.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for fastapi_validation_override-0.1.1.tar.gz
Algorithm Hash digest
SHA256 215a18fa9f17b080d286bb85e684f505d07bcd5f5bd8d8e7632c5024454dd6ff
MD5 4855f2895393bd26d64557ce5833b1fb
BLAKE2b-256 e2e7032f46ee8c61642b1fde4bee59d2821c67822876242b35f0020566350d6c

See more details on using hashes here.

File details

Details for the file fastapi_validation_override-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: fastapi_validation_override-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 6.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.18 {"installer":{"name":"uv","version":"0.11.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for fastapi_validation_override-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 373b6c9f22d046f6583d997d0632a024bdee1f59514d1ea54e19c71a5cc83bef
MD5 b5286a91303019b22c666838a021d71f
BLAKE2b-256 7d11879da0b786d2fd7ea999a96b8dff6e08024dbd824b3d7fc569f36a51b482

See more details on using hashes here.

Release history Release notifications | RSS feed

1.2.1

2 files

1.2.0

2 files

1.1.1

2 files

1.1.0

2 files

1.0.0

2 files

0.2.0

2 files

0.1.3

2 files

0.1.2

2 files

This release

0.1.1 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page