Main Ecosystems
HomePython CorePandas ReferenceNumPy ScientificFastAPI & PydanticDjango Enterprise
More Ecosystems
Environment & SetupRequests & HTTPAsyncIO ConcurrencyObject-Oriented OOPPyTorch Deep LearningScikit-Learn MLFlask FrameworkWeb ScrapingDatabase & ORMDevOps & Docker

FastAPI 422 Unprocessable Entity on Optional Query Parameters

Verified FixPython 3.10+FastAPI 0.110+ / Pydantic 2.6+Silo: fastapi

Quick Fix / Solution Rapide

Set default value default=None in query parameter definitions (limit: int | None = None or Query(default=None)) so missing parameters do not fail validation.

Root Cause Analysis

This error occurs when a client sends an HTTP request to a FastAPI endpoint omitting a query parameter, but FastAPI returns HTTP 422 Unprocessable Entity because the parameter was defined with type int (or str) without an explicit = None default value.

1. Type Annotation Without Default Value Equals Required in FastAPI

In FastAPI and Pydantic, declaring def get_items(limit: int): marks limit as a mandatory query parameter. Even if annotated as limit: Optional[int], omitting the default = None still marks the parameter as required by OpenAPI specifications.

2. Pydantic 2.0 Strict Validation Rules

In Pydantic 2.0+, limit: Optional[int] without = None means 'value may be None or int, but the field MUST be provided in the payload'. To make the field truly optional when omitted from HTTP requests, an explicit default = None is mandatory.

3. Query Parameter Coercion Failures

Clients passing empty strings (e.g. ?limit=) for numeric parameters cause Pydantic coercion errors.

4. Swagger UI Documentation Inconsistencies

Omitting = None marks the query parameter with a red asterisk (*) in Swagger UI, confusing API consumers.

Reproduction Code (MCVE)

Example: Bug Reproduction
from pydantic import BaseModel

class QueryModel(BaseModel):
    limit: int  # Missing default=None makes parameter required

# Instantiating model without mandatory query parameter raises ValidationError
QueryModel()

Solution 1: Assign = None Default to Optional Query Parameters

Use int | None = None (Python 3.10+) or Optional[int] = None to mark parameters as truly optional.

Example: Recommended Solution
from typing import Optional

# Proper FastAPI route definition for optional query parameters
def get_items(search: str | None = None, limit: int | None = 10, offset: int = 0):
    params = {'search': search, 'limit': limit, 'offset': offset}
    print(f'Resolved query parameters: {params}')
    return params

print(get_items())  # Can be called without any arguments

Solution 2: Use FastAPI Query() Parameter Helper

Use Query(default=None) for advanced validation metadata (min_length, max_length, regex).

Example: Alternative Solution
class MockQuery:
    def __init__(self, default=None, **kwargs):
        self.default = default

def search_endpoint(q: str = MockQuery(default=None).default):
    return {'query': q}

print(search_endpoint())

A common mistake is writing q: Optional[str] without = None. Always write q: Optional[str] = None or q: str | None = None. Edge cases occur with Boolean query parameters: clients sending ?active=false or ?active=0 are coerced to False, but sending ?active= (empty string) will fail validation unless active: bool | None = None is defined. Contrast this error with 400 Bad Request, which is returned for custom business logic rejections rather than Pydantic schema validation failures.