nautechsystems/nautilus_trader · error · anyhow::Error

Instances must have encode_record_batch_py method

Error message

Instances must have encode_record_batch_py method

What it means

When encoding a custom data class to an Arrow RecordBatch via py_encode_custom_data_to_record_batch, instances are expected to expose a Python method encode_record_batch_py. If the registered Python data class lacks it, the function bails. This enforces the EncodeToRecordBatch contract on user-defined custom data types.

Source

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

            py_batch.call_method1(
                "_export_to_c",
                (
                    (&raw mut ffi_array as usize),
                    (&raw mut ffi_schema as usize),
                ),
            )?;

            let schema = std::sync::Arc::new(arrow::datatypes::Schema::try_from(&ffi_schema)?);
            let struct_array_data = unsafe {
                arrow::ffi::from_ffi_and_data_type(
                    ffi_array,
                    arrow::datatypes::DataType::Struct(schema.fields().clone()),
                )?
            };
            let struct_array = arrow::array::StructArray::from(struct_array_data);
            Ok(arrow::record_batch::RecordBatch::from(&struct_array))
        } else {
            anyhow::bail!("Instances must have encode_record_batch_py method")
        }
    })
}

#[cfg(all(feature = "python", feature = "arrow"))]
fn pyarrow_schema_to_arrow_schema(
    py_schema: &pyo3::Bound<'_, pyo3::PyAny>,
) -> PyResult<arrow::datatypes::Schema> {
    let mut ffi_schema = arrow::ffi::FFI_ArrowSchema::empty();
    py_schema.call_method1("_export_to_c", ((&raw mut ffi_schema as usize),))?;
    arrow::datatypes::Schema::try_from(&ffi_schema)
        .map_err(|e| to_pyvalue_err(format!("Failed to import PyArrow schema: {e}")))
}

/// Decodes `RecordBatch` to `CustomData` via Python `decode_record_batch_py`.
#[allow(unsafe_code)]
#[cfg(all(feature = "python", feature = "arrow"))]
fn py_decode_record_batch_to_custom_data(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Define the custom data class using the nautilus custom data macro/base class so encode_record_batch_py is generated.
  2. Add or restore the encode_record_batch_py method to the class implementing its Arrow schema/batch encoding.
  3. Check library version migration notes: the Rust serialization path requires this method where the old path may not have.
  4. Register the class via register_custom_data_class only after the method exists (e.g. assert hasattr(cls, 'encode_record_batch_py')).

Example fix

# before
@customdataclass
class MyData(Data):  # lacks batch encoder
    ...
# after
class MyData(Data):
    @classmethod
    def encode_record_batch_py(cls, items):
        return MyDataEncoder.items_to_record_batch(items)  # matches declared arrow schema
Defensive patterns

Strategy: type-guard

Validate before calling

# Python: check before registering
if not hasattr(MyData, "encode_record_batch_py"):
    raise TypeError("MyData must implement encode_record_batch_py before register_custom_data_class")

Type guard

def has_batch_encoder(cls) -> bool:
    return callable(getattr(cls, "encode_record_batch_py", None))

Try / catch

try:
    register_custom_data_class(MyData)
except Exception as e:
    logging.error("custom data registration failed: %s", e)

Prevention

When it happens

Trigger: register_custom_data_class called with a Python class whose instances do not implement encode_record_batch_py — e.g. a plain dataclass not decorated/registered through the nautilus custom data macro, or a class defining only a module-level encode function.

Common situations: Python users subclassing nautilus_trader.model.custom without using the required custom data base/macro; renaming or removing encode_record_batch_py after a library version upgrade; mixing Cython and Rust code paths where only one defines the method.

Related errors


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