nautechsystems/nautilus_trader · error
Failed to convert to Python: {e}
Error message
Failed to convert to Python: {e} What it means
When encoding custom data items to an Arrow RecordBatch, each Rust-side item (`Arc<dyn CustomDataTrait>`) is first converted to its Python representation via `to_pyobject`. This error is raised if any item fails that Rust→Python conversion; the underlying pyo3/Python error is embedded in `{e}`. It aborts the whole batch encode — no partial output is produced.
Source
Thrown at crates/model/src/python/data/mod.rs:382
.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
.first()
.ok_or_else(|| anyhow::anyhow!("No items to encode"))?;
let first_py = first.to_pyobject(py)?;
if first_py
.bind(py)
.hasattr("encode_record_batch_py")
.unwrap_or(false)
{
let py_batch = first_py
.bind(py)
.call_method1("encode_record_batch_py", (py_list,))
.map_err(|e| anyhow::anyhow!("Failed to call encode_record_batch_py: {e}"))?;
View on GitHub (pinned to 18893faf8b)
Solutions
- Read the embedded `{e}` message to identify which item and why the conversion failed.
- Ensure all items in the batch are live, valid instances of the registered custom data class before encoding.
- Re-create the items (decode again from source) rather than reusing wrappers across interpreter lifecycles.
- Encode items in smaller batches to isolate the failing item.
Defensive patterns
Strategy: validation
Validate before calling
assert all(isinstance(x, MyClass) for x in items), "all items must be live registered-class instances"
Try / catch
try:
batch = encode_to_record_batch(items)
except Exception as e:
logger.error(f"item -> Python conversion failed: {e}")
raise Prevention
- Do not reuse wrappers across interpreter lifecycles
- Re-decode items from source rather than caching stale Python-backed objects
- Encode homogeneous batches of one registered class
When it happens
Trigger: Calling the Arrow encode path for registered custom data (via `register_custom_data_class`-registered types) when one or more items in the slice fail `to_pyobject(py)` — typically because the wrapper holds a dead/invalid Python reference or the item's conversion method raises.
Common situations: Items were decoded from a stream whose Python objects were garbage-collected or the interpreter state changed; a custom wrapper's `to_pyobject` implementation raises; mixing items whose underlying Python class no longer matches the registered class after hot-reload.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Failed to create list: {e}
- Failed to convert historical data to Python: unsupported typ
- Instances must have encode_record_batch_py method
- Failed to convert instrument to Python: {e}
- Failed to convert InstrumentAny to Python: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/7814200d41d572d5.
Report an issue: GitHub.