{"record":{"id":"b5f8246995a115c7","repo":"nautechsystems/nautilus_trader","slug":"expected-was-different-type","errorCode":null,"errorMessage":"Expected {}, was different type","messagePattern":"Expected (.+?), was different type","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/persistence/macros/src/custom.rs","lineNumber":966,"sourceCode":"    let generics = ctx.generics;\n    let name_str = ctx.name_str;\n    quote! {\n        impl #generics nautilus_serialization::arrow::custom::CustomDataSerialize for #name #generics {\n            fn schema(&self) -> anyhow::Result<arrow::datatypes::Schema> {\n                Ok(<Self as nautilus_serialization::arrow::ArrowSchemaProvider>::get_schema(\n                    Some(nautilus_serialization::arrow::EncodeToRecordBatch::metadata(self))\n                ).into())\n            }\n            fn encode_record_batch(\n                &self,\n                items: &[std::sync::Arc<dyn nautilus_model::data::CustomDataTrait>],\n            ) -> anyhow::Result<arrow::record_batch::RecordBatch> {\n                let mut typed: Vec<Self> = Vec::with_capacity(items.len());\n                for item in items {\n                    if let Some(c) = item.as_any().downcast_ref::<Self>() {\n                        typed.push(std::clone::Clone::clone(c));\n                    } else {\n                        anyhow::bail!(\"Expected {}, was different type\", #name_str);\n                    }\n                }\n                let metadata = nautilus_serialization::arrow::EncodeToRecordBatch::metadata(self);\n                nautilus_serialization::arrow::EncodeToRecordBatch::encode_batch(&metadata, &typed).map_err(Into::into)\n            }\n        }\n    }\n}\n\nfn gen_arrow_schema_impl(ctx: &ExpansionContext<'_>) -> TokenStream {\n    let name = ctx.name;\n    let generics = ctx.generics;\n    let field_list = ctx.field_list;\n    let arrow_schema_fields: Vec<TokenStream> = field_list\n        .iter()\n        .map(|f| {\n            let ident = &f.ident;\n            let ty = &f.ty;","sourceCodeStart":948,"sourceCodeEnd":984,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/persistence/macros/src/custom.rs#L948-L984","documentation":"The #[custom_data] macro generates a serialize/encode implementation that downcasts each incoming Data trait object to the concrete type Self. If any item is not actually an instance of the macro-generated type, generation bails with 'Expected {TypeName}, was different type', protecting the batch encoder from mixing incompatible payloads.","triggerScenarios":"expand_custom_data / gen_custom_data_serialize_impl emitting code that is later called with a Vec of Data objects containing items of another custom data type — e.g. encoding a batch that mixes MyData and OtherData, or passing the wrong subscription's messages to the encoder.","commonSituations":"Publishing two different custom data types through one queue/topic and then encoding them together; generic pipeline code assuming a homogeneous Vec<Data>; registration wiring that associates the wrong data type with an encoder.","solutions":["Ensure the batch passed to the generated encode function contains only instances of that exact custom data type.","Separate publishing/encoding paths per data type instead of funneling all Data through one encoder.","Check the downcast at the producer side: filter or split items by concrete type before batching.","Verify custom data registration maps each type to its own encoder metadata."],"exampleFix":"// before\nlet batch = my_data_encode(all_items); // all_items mixes Data types\n// after\nlet typed: Vec<MyData> = all_items.into_iter().filter_map(|d| d.as_any().downcast_ref::<MyData>().cloned()).collect();\nlet batch = my_data_encode(typed);","handlingStrategy":"type-guard","validationCode":"// Rust: filter to the concrete type before encoding\nlet typed: Vec<MyData> = items.into_iter()\n    .filter_map(|d| d.as_any().downcast_ref::<MyData>().cloned())\n    .collect();","typeGuard":"fn is_my_data(d: &dyn Data) -> bool { d.as_any().downcast_ref::<MyData>().is_some() }","tryCatchPattern":"match encoder.encode_batch(items) {\n    Ok(batch) => batch,\n    Err(e) => { log::error!(\"encode failed (mixed types?): {e}\"); split_by_type_and_retry(items) },\n}","preventionTips":["Keep one encode path per custom data type; never mix Data subtypes in a batch.","Split or filter incoming Data by concrete type before batching.","Check topic/queue wiring so each type's messages reach its own encoder.","Add round-trip tests (encode then decode) per custom data type."],"tags":["rust","macros","arrow","custom-data","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}