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

  1. Implement a to_json() method on the Python class returning a JSON string.
  2. Alternatively ensure the object is a normal class with instance attributes so __dict__ exists (remove __slots__ or add a dict-backed base).
  3. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/2b72ffe9e4237b9f. Report an issue: GitHub.