← Debugging Guides

阅读中文版本

Python TypeError — Object Is Not JSON Serializable

Fix "Object of type X is not JSON serializable" by providing a default handler or converting dates, sets, and custom objects before dumping.

intermediatedebuggingpythonjsonserialization

Symptoms

  • TypeError: Object of type datetime is not JSON serializable
  • json.dumps works until a response contains a date or set
  • A web framework returns 500 on serializing a model object

Possible causes

  • The value contains datetime, set, Decimal, UUID, or bytes not understood by the default encoder
  • An ORM or class instance is passed instead of plain dict/list values
  • A custom class lacks a to_dict or ISO conversion at the boundary

Diagnosis steps

Reproduce with the offending value

python -c "import json, datetime; json.dumps({'t': datetime.datetime.now()})"

Interpretation

The error names the exact type, which tells you what conversion is needed.

Add a default handler and retest

json.dumps(obj, default=lambda o: o.isoformat() if hasattr(o,'isoformat') else str(o))

Interpretation

A default handler converts unknown types; ISO-format dates are the common choice.

Root cause

The standard JSON encoder supports only dict, list, str, int, float, bool, and None. Any other type raises TypeError at encode time unless a default callable or custom encoder converts it.

Fix

Convert values at the boundary: format datetimes as ISO strings, convert sets and Decimals to list/float, UUID to str, and ORM models to dicts. Provide a single default handler so conversion is consistent.

Risk note

str() fallbacks can leak unexpected representations; prefer explicit per-type conversions over a catch-all str.

Verify the fix

Re-dump the payload and confirm valid JSON; parse it back and assert the date round-trips as an ISO string. Add a test covering each custom type.

Symptoms

The error “Python TypeError — Object Is Not JSON Serializable” points at a specific failure mode. This guide documents the symptoms you can observe, the commands that isolate the cause, the root cause, the fix, and the checks that prove the fix works.

Observed symptoms include:

  • TypeError: Object of type datetime is not JSON serializable
  • json.dumps works until a response contains a date or set
  • A web framework returns 500 on serializing a model object

Reproduce and Diagnose

Reproduce the failure in isolation before changing any code. Work through the checks below in order; each one narrows the search space.

Step 1: Reproduce with the offending value

python -c "import json, datetime; json.dumps({'t': datetime.datetime.now()})"

The error names the exact type, which tells you what conversion is needed.

Step 2: Add a default handler and retest

json.dumps(obj, default=lambda o: o.isoformat() if hasattr(o,'isoformat') else str(o))

A default handler converts unknown types; ISO-format dates are the common choice.

Likely contributors, in order of frequency:

  • The value contains datetime, set, Decimal, UUID, or bytes not understood by the default encoder
  • An ORM or class instance is passed instead of plain dict/list values
  • A custom class lacks a to_dict or ISO conversion at the boundary

Root Cause

The standard JSON encoder supports only dict, list, str, int, float, bool, and None. Any other type raises TypeError at encode time unless a default callable or custom encoder converts it.

The Fix

Convert values at the boundary: format datetimes as ISO strings, convert sets and Decimals to list/float, UUID to str, and ORM models to dicts. Provide a single default handler so conversion is consistent.

Watch out: str() fallbacks can leak unexpected representations; prefer explicit per-type conversions over a catch-all str.

import json
from datetime import date, datetime
from decimal import Decimal
from uuid import UUID

def default_handler(o):
    if isinstance(o, (datetime, date)):
        return o.isoformat()
    if isinstance(o, (set, frozenset)):
        return sorted(o)
    if isinstance(o, (Decimal, UUID)):
        return str(o)
    raise TypeError(f'not serializable: {type(o).__name__}')

payload = {'id': UUID(int=0), 'at': datetime.now(), 'tags': {'a', 'b'}}
print(json.dumps(payload, default=default_handler))

Verify and Prevent

Re-dump the payload and confirm valid JSON; parse it back and assert the date round-trips as an ISO string. Add a test covering each custom type.

Once verified, add a regression test that reproduces the original symptom so the failure cannot silently return.

This class of failure is usually environmental rather than pure logic, so capture the exact command, the environment variables, and the working directory in your bug report. Reproduce it in the smallest possible script, then widen the scope only once the minimal case is understood. A short written checklist for this failure mode saves hours the next time it appears.