nautechsystems/nautilus_trader · error
Python object has no to_json() method or __dict__ attribute
Error message
Python object has no to_json() method or __dict__ attribute
What it means
CustomData::to_json serializes a Python-backed custom data object. It first tries the object's to_json() method, then falls back to json.dumps of the object's __dict__. If the Python object has neither a to_json() method nor a __dict__ attribute, this error is raised because no serialization path exists.
Source
Thrown at crates/model/src/data/custom.rs:184
fn ts_event(&self) -> UnixNanos {
self.cached_ts_event
}
fn to_json(&self) -> anyhow::Result<String> {
Python::attach(|py| {
let obj = self.py_object.bind(py);
// Call to_json() on the Python object if available
if obj.hasattr("to_json")? {
let json_str: String = obj.call_method0("to_json")?.extract()?;
Ok(json_str)
} else {
// Fallback: use Python's json module
let json_module = py.import("json")?;
// Try to get a dict representation
let dict = if obj.hasattr("__dict__")? {
obj.getattr("__dict__")?
} else {
anyhow::bail!("Python object has no to_json() method or __dict__ attribute");
};
let json_str: String = json_module.call_method1("dumps", (dict,))?.extract()?;
Ok(json_str)
}
})
}
fn clone_arc(&self) -> Arc<dyn CustomDataTrait> {
Arc::new(self.clone())
}
fn eq_arc(&self, other: &dyn CustomDataTrait) -> bool {
// Equality by Python object identity only, to avoid false equality when two
// distinct Python objects share the same type name and timestamps.
if let Some(other_wrapper) = other.as_any().downcast_ref::<Self>() {
Python::attach(|py| {
let a = self.py_object.bind(py);
let b = other_wrapper.py_object.bind(py);View on GitHub (pinned to 18893faf8b)
Solutions
- Implement a to_json() method on the Python class returning a JSON string.
- Alternatively ensure the object is a normal class with instance attributes so __dict__ exists (remove __slots__ or add a dict-backed base).
- Wrap the value in a simple serializable container class exposing to_json().
Example fix
// before
class MyData:
__slots__ = ("x",)
def __init__(self, x): self.x = x
// after
class MyData:
def __init__(self, x): self.x = x
def to_json(self) -> str:
import json
return json.dumps({"x": self.x}) Defensive patterns
Strategy: type-guard
Validate before calling
# Python
def is_json_serializable_custom_data(obj) -> bool:
return hasattr(obj, "to_json") or hasattr(obj, "__dict__") Type guard
# Python
def has_json_repr(obj) -> bool:
return callable(getattr(obj, "to_json", None)) or hasattr(obj, "__dict__") Try / catch
# Python
try:
data.to_json()
except ValueError as e:
if "to_json() method or __dict__" in str(e):
raise TypeError(f"{type(data).__name__} must define to_json() or use a plain class") from e
raise Prevention
- Always implement to_json() on classes passed as custom data.
- Avoid __slots__-only classes for custom data payloads.
- Add a serialization round-trip test for every custom data type.
When it happens
Trigger: Wrapping a Python object (typically one implemented with __slots__, a builtin type, or a Rust/exotic type without instance dict) in custom data and calling to_json()/to_json_py() on it.
Common situations: Using classes with __slots__ for memory efficiency; passing built-in objects (e.g. datetime without wrapping) as custom data; porting older custom data classes that never defined to_json.
Understand the failure class
Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.
Related errors
- Instances must have encode_record_batch_py method
- Failed to import json: {e}
- Failed to parse JSON: {e}
- Failed to call from_json: {e}
- JSON does not represent CustomData
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/2b72ffe9e4237b9f.
Report an issue: GitHub.