ValidationError: value is not a valid dict in FastAPI & Pydantic
Ensure requests send valid JSON with header Content-Type: application/json matching the Pydantic schema model.
Root Cause Analysis
This error occurs when Python tries to validate incoming HTTP request data against a Pydantic model in FastAPI, but the parsed input is a string, list, or scalar instead of a valid key-value dictionary.
1. Pydantic Body Validation Protocol
In FastAPI and Pydantic v2, when a route endpoint declares a request body model item: ItemPayload, the framework expects the raw HTTP request body to be a valid JSON object (deserialized into a Python dict). If the incoming data is a plain string, integer, or array, Pydantic's core validator rejects the payload with ValidationError: Input should be a valid dictionary or instance to extract fields from.
2. Common Causes in API Clients
This error typically arises when: (1) an API client sends a JSON string double-encoded as a string literal, (2) the HTTP request header Content-Type is missing or set to text/plain rather than application/json, (3) client code passes a list of items [...] instead of a single object {...}, or (4) a query parameter is mistakenly configured as a body model.
3. Pydantic v1 vs Pydantic v2 Differences
In Pydantic v1, the error message was formatted as value is not a valid dict. In Pydantic v2, the message is Input should be a valid dictionary or instance to extract fields from. Both indicate that the schema expected a dictionary mapping but received an incompatible type.
4. Ensuring Clean API Ingestion
To fix this error, ensure the client serializes payloads as proper JSON dictionaries and use FastAPI's Body(...) or Query(...) annotations to distinguish body payloads from query parameters.
Reproduction Code (MCVE)
from pydantic import BaseModel, ValidationError
class ItemPayload(BaseModel):
name: str
price: float
# ValidationError: Input should be a valid dictionary or instance to extract fields from
raw_invalid_input = 'this is a raw string, not a dictionary'
ItemPayload.model_validate(raw_invalid_input)
Solution 1: Pass a Valid Key-Value Dictionary to Pydantic
Ensure incoming payloads are properly deserialized into Python dictionaries before invoking Pydantic validation.
import json
from pydantic import BaseModel
class ItemPayload(BaseModel):
name: str
price: float
# Ensure payload is parsed as a valid dictionary
json_str = '{"name": "Wireless Mouse", "price": 29.99}'
dict_data = json.loads(json_str)
item = ItemPayload.model_validate(dict_data)
print(f'Validated successfully: {item.name} (${item.price})')
Solution 2: Use model_validate_json for Direct JSON String Parsing
In Pydantic v2, use model_validate_json() to parse and validate raw JSON strings directly with optimized C/Rust performance.
from pydantic import BaseModel
class ItemPayload(BaseModel):
name: str
price: float
# Direct parsing from raw JSON string
raw_json = '{"name": "Mechanical Keyboard", "price": 129.50}'
item = ItemPayload.model_validate_json(raw_json)
print(f'Validated via model_validate_json: {item.name} (${item.price})')
A common mistake when using FastAPI is forgetting the Content-Type: application/json header in curl or Postman requests, causing FastAPI to misinterpret the body. Contrast ValidationError (semantic schema violation) with json.decoder.JSONDecodeError (malformed JSON syntax like trailing commas). When expecting an array of items, define the endpoint parameter as items: list[ItemPayload] rather than ItemPayload alone. In Pydantic v2, avoid legacy v1 methods like parse_obj() and use model_validate() instead.