Main Ecosystems
HomePython CorePandas ReferenceNumPy ScientificFastAPI & PydanticDjango Enterprise
More Ecosystems
Environment & SetupRequests & HTTPAsyncIO ConcurrencyObject-Oriented OOPPyTorch Deep LearningScikit-Learn MLFlask FrameworkWeb ScrapingDatabase & ORMDevOps & Docker

TypeError: Object of type ObjectId is not JSON serializable in Python

Verified FixPython 3.10+Python Native json / bsonSilo: core

Quick Fix / Solution Rapide

Use json.dumps(data, default=str) for quick conversion, or create a custom json.JSONEncoder subclass to handle ObjectId, datetime, and UUID objects.

Root Cause Analysis

This error occurs when Python's built-in json.dumps() function attempts to serialize a dictionary containing custom non-native objects (such as MongoDB bson.ObjectId, datetime.datetime, decimal.Decimal, or uuid.UUID), but Python's default JSON encoder only recognizes native primitives (dict, list, str, int, float, bool, and None).

Python's Built-in JSON Encoder Architecture

The json module implements strict JSON data specification types. When json.dumps() encounters an object whose class is not in the built-in primitive dispatch table:

  1. It calls JSONEncoder.default(o).
  2. The default implementation raises TypeError: Object of type X is not JSON serializable.

Common Root Causes

  • Querying MongoDB via PyMongo: collection.find_one() returns documents with _id: ObjectId('...').
  • Datetime Objects in API Responses: Attempting to serialize datetime.now() directly without .isoformat().
  • SQLAlchemy / Pydantic Models: Serializing model instances directly rather than .model_dump() or model dictionaries.

Reproduction Code (MCVE)

Example: Bug Reproduction
import json

# Simulates custom ObjectId object passed to json.dumps()
class ObjectId:
    def __init__(self, val='507f1f77bcf86cd799439011'):
        self.val = val

data = {'_id': ObjectId(), 'name': 'Sample Document'}
json.dumps(data)

Solution 1: Use `default=str` in `json.dumps()`

Pass default=str to json.dumps() to automatically convert any non-serializable objects into string representations.

Example: Recommended Solution
import json
from datetime import datetime

class ObjectId:
    def __init__(self, val='507f1f77bcf86cd799439011'):
        self.val = val
    def __str__(self):
        return self.val

document = {
    '_id': ObjectId(),
    'title': 'Production Order',
    'created_at': datetime(2026, 9, 8, 12, 0, 0)
}

# Solution: use default=str for seamless JSON conversion
json_output = json.dumps(document, default=str)
print(f'Successfully serialized JSON: {json_output}')

Solution 2: Create a Custom `json.JSONEncoder` Subclass

Subclass json.JSONEncoder for granular control over datetimes, ObjectIds, and Decimals.

Example: Alternative Solution
import json
from datetime import datetime
from decimal import Decimal

class AdvancedJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        if isinstance(obj, Decimal):
            return float(obj)
        if hasattr(obj, '__str__'):
            return str(obj)
        return super().default(obj)

payload = {'price': Decimal('99.99'), 'timestamp': datetime(2026, 1, 1)}
print(json.dumps(payload, cls=AdvancedJSONEncoder))

Common Pitfalls & Edge Cases

When using default=str with complex nested objects, ensure circular references do not exist in the object graph (which would trigger ValueError: Circular reference detected). For MongoDB PyMongo applications, you can also use from bson.json_util import dumps for MongoDB Extended JSON format.

Contrasting with similar errors:

  • TypeError: Object of type X is not JSON serializable: Object cannot be converted to JSON string.
  • TypeError: the JSON object must be str, bytes or bytearray, not dict: Passing a dict to json.loads() instead of json.dumps().