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.

RuntimeMessageInput and evidence
Python 3.7+TypeError: Object of type datetime is not JSON serializablejson.dumps({'created': datetime(2026, 10, 15, 9, 30)})
Reproduced in Python 3.14.4
Python 3.14+, notewhen serializing dict item 'created'Printed under the TypeError
Reproduced in Python 3.14.4; notes were added in CPython 3.14
Python 3.6TypeError: Object of type 'datetime' is not JSON serializableThe same input
CPython 3.6.0 source template
Python 3.5 and earlierTypeError: datetime.datetime(2026, 10, 15, 9, 30) is not JSON serializableThe same input
Source-derived from CPython 3.5.0: the repr of the value, then the message
Python, dictionary keysTypeError: keys must be str, int, float, bool or None, not tuplejson.dumps({(1, 2): 'x'})
Reproduced in Python 3.14.4
Python 3.12+, NaNValueError: Out of range float values are not JSON compliant: nanjson.dumps({'x': float('nan')}, allow_nan=False)
Reproduced in Python 3.14.4
Python 3.11 and earlier, NaNValueError: Out of range float values are not JSON compliantThe same input
CPython 3.11.0 C encoder message, without the value
JavaScript / V8, reading Python NaNSyntaxError: Unexpected token 'N', "{"x": NaN}" is not valid JSONJSON.parse of json.dumps({'x': float('nan')}) output
Reproduced 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.

PYTHON
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.

PYTHON
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.

PYTHON
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

  1. Read the type name and, in Python 3.14 and later, the notes that locate the value.
  2. 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.
  3. Convert at the boundary: pass default= to json.dumps, subclass JSONEncoder, or turn your own objects into dicts first.
  4. 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.

PYTHON
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.

PYTHON
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.

PYTHON
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.

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.