nautechsystems/nautilus_trader · error · anyhow::Error
Expected Custom data variant
Error message
Expected Custom data variant
What it means
Generated by the `#[custom_data]` macro's `TryFrom<Data>` impl: the incoming `Data` enum was not the `Custom` variant at all (e.g. `Data::Instrument`, `Data::Trade`, `Data::OrderBookDeltas`), so conversion to the concrete custom type is impossible. The macro bails early instead of attempting a downcast on a non-custom payload.
Source
Thrown at crates/persistence/macros/src/custom.rs:1164
impl #generics std::convert::From<#name #generics> for nautilus_model::data::Data {
fn from(value: #name #generics) -> Self {
nautilus_model::data::Data::Custom(nautilus_model::data::CustomData::from_arc(std::sync::Arc::new(value)))
}
}
};
let try_from_impl = quote! {
impl #generics std::convert::TryFrom<nautilus_model::data::Data> for #name #generics {
type Error = anyhow::Error;
fn try_from(value: nautilus_model::data::Data) -> std::result::Result<Self, Self::Error> {
match value {
nautilus_model::data::Data::Custom(custom) => {
if let Some(c) = custom.data.as_any().downcast_ref::<Self>() {
Ok(std::clone::Clone::clone(c))
} else {
anyhow::bail!("Expected {}", #name_str)
}
}
_ => anyhow::bail!("Expected Custom data variant"),
}
}
}
};
(catalog_path_prefix_impl, from_impl, try_from_impl)
}
#[expect(
clippy::too_many_lines,
reason = "PyO3 token generation is clearer with related methods kept together"
)]
fn gen_pymethods_impl(ctx: &ExpansionContext<'_>) -> TokenStream {
let name = ctx.name;
let generics = ctx.generics;
let field_list = ctx.field_list;
if !ctx.options.pyo3 {
return quote! {};
}View on GitHub (pinned to 18893faf8b)
Solutions
- Match on the `Data` enum first and only convert `Data::Custom` payloads; handle other variants separately.
- Filter or partition the buffer by variant/type before attempting the custom conversion.
- Check upstream code that constructs the `Data` values to confirm the correct variant is produced.
Example fix
// before
let typed = MyType::try_from(data)?;
// after
let typed = match data {
nautilus_model::data::Data::Custom(_) => MyType::try_from(data)?,
_ => return Ok(None), // skip non-custom data
}; Defensive patterns
Strategy: type-guard
Validate before calling
fn is_custom(data: &nautilus_model::data::Data) -> bool {
matches!(data, nautilus_model::data::Data::Custom(_))
} Type guard
fn as_custom(data: &nautilus_model::data::Data) -> Option<&nautilus_model::data::CustomData> {
match data {
nautilus_model::data::Data::Custom(c) => Some(c),
_ => None,
}
} Try / catch
match MyType::try_from(data) {
Ok(t) => handle(t),
Err(e) if e.to_string() == "Expected Custom data variant" => Ok(()), // expected skip
Err(e) => return Err(e.into()),
} Prevention
- Filter buffers by `Data` variant before custom conversion
- Keep custom and standard data on separate channels/buffers
- Assert buffer homogeneity in tests
When it happens
Trigger: Passing a standard (non-custom) `Data` enum value into a macro-generated `TryFrom<T>` for a custom data type, e.g. feeding quote/trade data into a custom-data decode path or reading a buffer that mixes standard and custom data into the wrong consumer.
Common situations: A caller iterating a mixed `Data` buffer assumes all elements are its custom type; misrouted message channels delivering standard market data to a custom-data handler; test fixtures populating buffers with the wrong variant.
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
- Expected {}
- Cannot convert PoolId variant to Address
- Cannot convert Address variant to PoolId bytes
- Expected {}, was different type
- ts_init column is not UInt64
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/49a73cf74c02d6d3.
Report an issue: GitHub.