FastAPI Validation Override
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.openapifunction already installed and applies the patch on top of its output - Bring your own handler:
handle_exceptions=Falseskips 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=422leaves 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
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
215a18fa9f17b080d286bb85e684f505d07bcd5f5bd8d8e7632c5024454dd6ff
|
|
| MD5 |
4855f2895393bd26d64557ce5833b1fb
|
|
| BLAKE2b-256 |
e2e7032f46ee8c61642b1fde4bee59d2821c67822876242b35f0020566350d6c
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
373b6c9f22d046f6583d997d0632a024bdee1f59514d1ea54e19c71a5cc83bef
|
|
| MD5 |
b5286a91303019b22c666838a021d71f
|
|
| BLAKE2b-256 |
7d11879da0b786d2fd7ea999a96b8dff6e08024dbd824b3d7fc569f36a51b482
|