JSON troubleshooting
TypeError: Object of type … is not JSON serializable
Fix Python json.dumps errors for datetime, Decimal, set, bytes, UUID, NumPy values and custom classes, plus NaN values rejected with allow_nan=False.
What the error means
json.dumps converts only dict, list, tuple, str, int, float, bool and None, plus int- and float-derived enums. For any other object it calls the encoder's default method, which raises this TypeError unless you supply a conversion. The type name in the message tells you what to convert. Python 3.14 adds notes such as when serializing dict item 'created' that show where the value was found. A related ValueError, Out of range float values are not JSON compliant, appears for NaN and infinity when allow_nan=False.
Exact messages by runtime
Messages depend on the input and runtime version. Node and Python rows were reproduced locally. Other rows cite official documentation or source. Rows marked as fragments or templates preserve the source wording and are not complete exception transcripts. Browsers and libraries may append location details.
| Runtime | Message | Input and evidence |
|---|---|---|
| Python 3.7+ | TypeError: Object of type datetime is not JSON serializable | json.dumps({'created': datetime(2026, 10, 15, 9, 30)})Reproduced in Python 3.14.4 |
| Python 3.14+, note | when serializing dict item 'created' | Printed under the TypeErrorReproduced in Python 3.14.4; notes were added in CPython 3.14 |
| Python 3.6 | TypeError: Object of type 'datetime' is not JSON serializable | The same inputCPython 3.6.0 source template |
| Python 3.5 and earlier | TypeError: datetime.datetime(2026, 10, 15, 9, 30) is not JSON serializable | The same inputSource-derived from CPython 3.5.0: the repr of the value, then the message |
| Python, dictionary keys | TypeError: keys must be str, int, float, bool or None, not tuple | json.dumps({(1, 2): 'x'})Reproduced in Python 3.14.4 |
| Python 3.12+, NaN | ValueError: Out of range float values are not JSON compliant: nan | json.dumps({'x': float('nan')}, allow_nan=False)Reproduced in Python 3.14.4 |
| Python 3.11 and earlier, NaN | ValueError: Out of range float values are not JSON compliant | The same inputCPython 3.11.0 C encoder message, without the value |
| JavaScript / V8, reading Python NaN | SyntaxError: Unexpected token 'N', "{"x": NaN}" is not valid JSON | JSON.parse of json.dumps({'x': float('nan')}) outputReproduced in Node.js 24.21.0 |
Causes you can reproduce
Dates, Decimal and UUID values
datetime, date, Decimal and UUID have no JSON type. Database rows and ORM objects are full of them.
import json
from datetime import datetime
json.dumps({"created": datetime(2026, 10, 15, 9, 30)})
# TypeError: Object of type datetime is not JSON serializable
# when serializing dict item 'created'Sets, bytes, NumPy values and your own classes
A set has no JSON form, and bytes need an encoding such as base64. NumPy scalars report their own type names, such as int64 or float32, and arrays report ndarray; convert them with .item() and .tolist(). Instances of your own classes report the class name.
import json
json.dumps({"tags": {"admin", "staff"}})
# TypeError: Object of type set is not JSON serializable
# when serializing dict item 'tags'NaN or infinity with allow_nan=False
By default json.dumps writes NaN and Infinity, which strict JSON parsers reject. With allow_nan=False it raises instead. Starlette's JSONResponse serializes with allow_nan=False, so a NaN in response data, often from pandas, raises this error.
import json
json.dumps({"score": float("nan")}, allow_nan=False)
# ValueError: Out of range float values are not JSON compliant: nan
# when serializing dict item 'score'How to fix it
- Read the type name and, in Python 3.14 and later, the notes that locate the value.
- Choose a JSON form for that type: ISO 8601 strings for dates, strings for Decimal to keep every digit, lists for sets, base64 strings for bytes and str() for UUID.
- Convert at the boundary: pass default= to json.dumps, subclass JSONEncoder, or turn your own objects into dicts first.
- Replace NaN and infinity with None or a documented value before serializing, and keep allow_nan=False when the output must be standard JSON.
Convert known types with default=
json.dumps calls the function only for values it cannot serialize. Raise TypeError for anything you did not plan for. sorted() needs set items that can be compared.
import json
from datetime import date, datetime
from decimal import Decimal
from uuid import UUID
def to_json(value):
if isinstance(value, (datetime, date)):
return value.isoformat()
if isinstance(value, (Decimal, UUID)):
return str(value)
if isinstance(value, (set, frozenset)):
return sorted(value)
raise TypeError(f"Object of type {type(value).__name__} is not JSON serializable")
data = {"created": datetime(2026, 10, 15, 9, 30), "price": Decimal("9.99"),
"id": UUID(int=1), "tags": {"staff", "admin"}}
print(json.dumps(data, default=to_json))
# {"created": "2026-10-15T09:30:00", "price": "9.99", "id": "00000000-0000-0000-0000-000000000001", "tags": ["admin", "staff"]}Turn your own classes into dicts
dataclasses.asdict converts a dataclass, including nested dataclasses, into dicts and lists.
import json
from dataclasses import dataclass, asdict
@dataclass
class User:
name: str
active: bool
print(json.dumps(asdict(User("Ada", True)))) # {"name": "Ada", "active": true}Replace NaN and infinity before serializing
None becomes null. Use a different placeholder only if the receiver expects it.
import json
import math
row = {"score": float("nan"), "total": 3.5}
clean = {k: None if isinstance(v, float) and not math.isfinite(v) else v for k, v in row.items()}
print(json.dumps(clean, allow_nan=False)) # {"score": null, "total": 3.5}Frequently asked questions
Why not just use default=str?
It turns every unknown object into its str() text, including objects you did not mean to send, such as <__main__.User object at 0x...>. Handle the types you expect and raise TypeError for the rest.
How do I serialize NumPy values?
Convert scalars with .item() and arrays with .tolist() before calling json.dumps, or do it inside a default= function.
Why does json.dumps write NaN when JSON does not allow it?
allow_nan defaults to True, and Python then writes the JavaScript names NaN, Infinity and -Infinity. The Python documentation notes that this is not standard JSON. Set allow_nan=False to catch these values.
Should I subclass JSONEncoder or pass default=?
Both use the same hook. default= is simpler for one call. A JSONEncoder subclass with a default method is easier to reuse, for example through cls= or a framework setting.
Related errors and tools
- TypeError: Converting circular structure to JSON
- TypeError: Do not know how to serialize a BigInt
- All JSON error guides
- JSON syntax reference
- Generate Pydantic models from a JSON sample
Standards and diagnostic sources
RFC 8259 and ECMA-404 define JSON syntax. Diagnostic wording belongs to the parser. The table links each cited message to its source.