Skip to main content

FastAPI Validation Override

Build Status codecov 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.2.tar.gz (76.8 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.2-py3-none-any.whl (6.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: fastapi_validation_override-0.1.2.tar.gz
  • Upload date:
  • Size: 76.8 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.2.tar.gz
Algorithm Hash digest
SHA256 cfb87051ba91ad5c8e18b0876198efd2f5fda18d68f38f91bc056be0c3517e23
MD5 a0a8139bb9f43e96be0d29d947869146
BLAKE2b-256 3fbcad96edb9b8438602145978cd173192fd0de807fe0653b7822df3694f4ddb

See more details on using hashes here.

File details

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

File metadata

  • Download URL: fastapi_validation_override-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 6.4 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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 1b64d278d02b1430f64039daf6264d90579be979a7981b8a90bbf80599708b26
MD5 c24bea94b4230d359707f10766a416ea
BLAKE2b-256 fd4efd8a64162b2e3ec6cb089b61f87c6d2ddda78bfa4995abfd705f9d74794b

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

This release

0.1.2 This release

2 files

0.1.1

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