nautechsystems/nautilus_trader · error

Failed to create wrapper: {e}

Error message

Failed to create wrapper: {e}

What it means

When deserializing custom data from a JSON dict via a registered Python class, NautilusTrader calls the class's `from_json` method and then wraps the resulting Python object in `PythonCustomDataWrapper` so it can be stored as an `Arc<dyn CustomDataTrait>`. This error is raised when the wrapper construction itself fails, meaning the Python object returned by `from_json` is not a valid custom-data instance (e.g. missing required attributes/methods the wrapper expects). The underlying Python exception message is embedded in `{e}`.

Source

Thrown at crates/model/src/python/data/mod.rs:368

    use crate::data::PythonCustomDataWrapper;

    pyo3::Python::attach(|py| {
        let json_str = serde_json::to_string(&value)?;
        let json_module = py
            .import("json")
            .map_err(|e| anyhow::anyhow!("Failed to import json: {e}"))?;
        let py_dict = json_module
            .call_method1("loads", (json_str,))
            .map_err(|e| anyhow::anyhow!("Failed to parse JSON: {e}"))?;

        let instance = data_class
            .bind(py)
            .call_method1("from_json", (py_dict,))
            .map_err(|e| anyhow::anyhow!("Failed to call from_json: {e}"))?;

        let wrapper = PythonCustomDataWrapper::new(py, &instance)
            .map_err(|e| anyhow::anyhow!("Failed to create wrapper: {e}"))?;

        Ok(Arc::new(wrapper) as Arc<dyn crate::data::CustomDataTrait>)
    })
}

/// Encodes `CustomData` items to `RecordBatch` via Python `encode_record_batch_py`.
#[allow(unsafe_code)]
#[cfg(all(feature = "python", feature = "arrow"))]
fn py_encode_custom_data_to_record_batch(
    items: &[std::sync::Arc<dyn crate::data::CustomDataTrait>],
) -> Result<arrow::record_batch::RecordBatch, anyhow::Error> {
    pyo3::Python::attach(|py| {
        let py_items: Result<Vec<_>, _> = items.iter().map(|item| item.to_pyobject(py)).collect();
        let py_items = py_items.map_err(|e| anyhow::anyhow!("Failed to convert to Python: {e}"))?;
        let py_list = pyo3::types::PyList::new(py, &py_items)
            .map_err(|e| anyhow::anyhow!("Failed to create list: {e}"))?;

        let first = items

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the embedded `{e}` Python traceback: fix `from_json` so it returns a fully constructed instance of the registered data class (not a dict or None).
  2. Verify the registered class implements the full custom-data interface expected by PythonCustomDataWrapper (all required properties/methods like ts_init, type name, etc.).
  3. Call the class's `from_json` directly in Python with a sample dict and inspect the returned object's type and attributes.
  4. Re-register the class with `register_custom_data_class` after fixing it, and rerun the deserialization.

Example fix

# before
class MyData:
    @classmethod
    def from_json(cls, data):
        return data  # returns a dict -> wrapper fails

# after
class MyData:
    @classmethod
    def from_json(cls, data):
        return cls(**data)  # returns a proper instance
Defensive patterns

Strategy: try-catch

Validate before calling

obj = MyClass.from_json(sample_dict)
assert isinstance(obj, MyClass), f"from_json returned {type(obj)}"
assert hasattr(obj, "ts_init")

Type guard

def is_valid_custom_data(obj) -> bool:
    return isinstance(obj, MyClass) and hasattr(obj, "ts_init")

Try / catch

try:
    item = Data.from_json(payload, MyData)
except Exception as e:
    logger.error(f"custom data wrap failed: {e}")
    raise ValueError("from_json must return a MyData instance") from e

Prevention

When it happens

Trigger: Calling a deserialization path (via `register_custom_data_class`-registered types, e.g. `Data.from_json`/custom data decode) where the registered Python class's `from_json` returns an object that `PythonCustomDataWrapper::new` cannot wrap — such as a plain dict, None, or an object lacking the expected nautilus custom-data interface.

Common situations: A user-defined data class was registered but its `from_json` implementation returns the wrong type (dict instead of an instance, or None on parse failure); after upgrading nautilus the wrapper contract gained new required methods the old class does not implement; class returns an instance of a different class than the registered one.

Related errors


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