{"record":{"id":"1948091cbd01f4c5","repo":"nautechsystems/nautilus_trader","slug":"instances-must-have-encode-record-batch-py-method","errorCode":null,"errorMessage":"Instances must have encode_record_batch_py method","messagePattern":"Instances must have encode_record_batch_py method","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/model/src/python/data/mod.rs","lineNumber":422,"sourceCode":"            py_batch.call_method1(\n                \"_export_to_c\",\n                (\n                    (&raw mut ffi_array as usize),\n                    (&raw mut ffi_schema as usize),\n                ),\n            )?;\n\n            let schema = std::sync::Arc::new(arrow::datatypes::Schema::try_from(&ffi_schema)?);\n            let struct_array_data = unsafe {\n                arrow::ffi::from_ffi_and_data_type(\n                    ffi_array,\n                    arrow::datatypes::DataType::Struct(schema.fields().clone()),\n                )?\n            };\n            let struct_array = arrow::array::StructArray::from(struct_array_data);\n            Ok(arrow::record_batch::RecordBatch::from(&struct_array))\n        } else {\n            anyhow::bail!(\"Instances must have encode_record_batch_py method\")\n        }\n    })\n}\n\n#[cfg(all(feature = \"python\", feature = \"arrow\"))]\nfn pyarrow_schema_to_arrow_schema(\n    py_schema: &pyo3::Bound<'_, pyo3::PyAny>,\n) -> PyResult<arrow::datatypes::Schema> {\n    let mut ffi_schema = arrow::ffi::FFI_ArrowSchema::empty();\n    py_schema.call_method1(\"_export_to_c\", ((&raw mut ffi_schema as usize),))?;\n    arrow::datatypes::Schema::try_from(&ffi_schema)\n        .map_err(|e| to_pyvalue_err(format!(\"Failed to import PyArrow schema: {e}\")))\n}\n\n/// Decodes `RecordBatch` to `CustomData` via Python `decode_record_batch_py`.\n#[allow(unsafe_code)]\n#[cfg(all(feature = \"python\", feature = \"arrow\"))]\nfn py_decode_record_batch_to_custom_data(","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/python/data/mod.rs#L404-L440","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Define the custom data class using the nautilus custom data macro/base class so encode_record_batch_py is generated.","Add or restore the encode_record_batch_py method to the class implementing its Arrow schema/batch encoding.","Check library version migration notes: the Rust serialization path requires this method where the old path may not have.","Register the class via register_custom_data_class only after the method exists (e.g. assert hasattr(cls, 'encode_record_batch_py'))."],"exampleFix":"# before\n@customdataclass\nclass MyData(Data):  # lacks batch encoder\n    ...\n# after\nclass MyData(Data):\n    @classmethod\n    def encode_record_batch_py(cls, items):\n        return MyDataEncoder.items_to_record_batch(items)  # matches declared arrow schema","handlingStrategy":"type-guard","validationCode":"# Python: check before registering\nif not hasattr(MyData, \"encode_record_batch_py\"):\n    raise TypeError(\"MyData must implement encode_record_batch_py before register_custom_data_class\")","typeGuard":"def has_batch_encoder(cls) -> bool:\n    return callable(getattr(cls, \"encode_record_batch_py\", None))","tryCatchPattern":"try:\n    register_custom_data_class(MyData)\nexcept Exception as e:\n    logging.error(\"custom data registration failed: %s\", e)","preventionTips":["Always define custom data via the nautilus custom data macro/base class.","Check migration notes after upgrading nautilus versions.","Smoke-test register + encode round-trip for each custom type in CI.","Don't strip generated methods when subclassing."],"tags":["rust","python","arrow","custom-data","serialization"],"backgroundTag":"method-not-implemented","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}