TypeError: Object of type ObjectId is not JSON serializable in Python
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:
- It calls
JSONEncoder.default(o). - 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)
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.
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.
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 tojson.loads()instead ofjson.dumps().