tiangolo/fastapi · error · ValueError
{errors}
Error message
{errors} What it means
ValueError raised by jsonable_encoder as a last resort (encoders.py:355). After the object matches no registered encoder, is not a pydantic v1 model, and both dict(obj) and vars(obj) raise, FastAPI collects the two caught exceptions into a list and raises ValueError(errors) from the last one. The object is simply not JSON-encodable by FastAPI's rules.
Source
Thrown at fastapi/encoders.py:355
return ENCODERS_BY_TYPE[type(obj)](obj)
for encoder, classes_tuple in encoders_by_class_tuples.items():
if isinstance(obj, classes_tuple):
return encoder(obj)
if is_pydantic_v1_model_instance(obj):
raise PydanticV1NotSupportedError(
"pydantic.v1 models are no longer supported by FastAPI."
f" Please update the model {obj!r}."
)
try:
data = dict(obj)
except Exception as e:
errors: list[Exception] = []
errors.append(e)
try:
data = vars(obj)
except Exception as e:
errors.append(e)
raise ValueError(errors) from e
return jsonable_encoder(
data,
include=include,
exclude=exclude,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
custom_encoder=custom_encoder,
sqlalchemy_safe=sqlalchemy_safe,
)
View on GitHub (pinned to 3e8d1526d8)
Solutions
- Declare a pydantic response_model so FastAPI serializes the object through the model instead of duck-typing it.
- Convert the object to a dict/list of primitives before returning (e.g. [h.model_dump() for h in rows]).
- Add a custom_encoder for the specific type, or pass json_encoders via response_model.
- Implement asdict()/__iter__ (to make dict(obj) work) or a normal __dict__ on the class.
Example fix
// before
@app.get("/widgets/{i}")
def read(i: int):
return session.get(Widget, i) # raw ORM object, dict()/vars() both fail
# -> ValueError([...])
// after
class WidgetOut(BaseModel):
id: int
name: str
@app.get("/widgets/{i}", response_model=WidgetOut)
def read(i: int):
return session.get(Widget, i) Defensive patterns
Strategy: try-catch
Validate before calling
# Verify an object is jsonable_encoder-friendly before returning it
from fastapi.encoders import jsonable_encoder
def safe(obj):
try:
jsonable_encoder(obj)
return obj
except Exception:
# convert ORM/dataclass to dict before returning
if hasattr(obj, "model_dump"):
return obj.model_dump()
if hasattr(obj, "__dict__"):
return vars(obj)
raise Type guard
def is_jsonable(obj) -> bool:
try:
jsonable_encoder(obj)
except Exception:
return False
return True Try / catch
from fastapi.encoders import jsonable_encoder
try:
payload = jsonable_encoder(result)
except ValueError:
payload = [r.model_dump() for r in result] if isinstance(result, list) else result.model_dump() Prevention
- Always declare a pydantic response_model for ORM/data sources.
- Return model_dump()/dict() of raw objects instead of ORM rows.
- Add custom encoders for niche types (Decimal, UUID, enum).
- Write a TestClient test that hits each endpoint and asserts a 200 JSON body.
When it happens
Trigger: Returning an object from an endpoint that is not a dict/list/primitive/pydantic-model/known type, and that does not support dict() or __dict__ (vars). For example a SQLAlchemy 2.x ORM object without a pydantic response_model, a custom class with __slots__ and no mapping protocol, an open file/regex/lock object, or an unserializable nested value.
Common situations: Returning raw SQLAlchemy/SQLModel ORM rows without a response_model that converts them; a custom dataclass-like object with __slots__; an enum or type object slipping into the response; a relationship/collection that is not JSON-coercible; a non-encodable value (datetime.tzinfo, Decimal without encoder, UUID in a raw object).
Related errors
- pydantic.v1 models are no longer supported by FastAPI. Pleas
- {n} validation error(s): {errors}
- pydantic.v1 models are no longer supported by FastAPI. Pleas
- Invalid X-Token header
- Item not found
AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11).
Data as JSON: /api/errors/580d10f3dee5e4b5.
Report an issue: GitHub.