nautechsystems/nautilus_trader · error
`InstrumentId` not applicable to `Block`
Error message
`InstrumentId` not applicable to `Block`
What it means
Data::instrument_id() panics for variants that conceptually have no instrument ID — Block and PoolSnapshot. The method is only meaningful for pool events (swaps, liquidity updates, fee events). The panic guards against silently returning a meaningless default identifier.
Source
Thrown at crates/model/src/defi/data/mod.rs:226
Self::PoolSnapshot(snapshot) => snapshot.ts_init,
Self::PoolSwap(swap) => swap.ts_init,
Self::PoolLiquidityUpdate(update) => update.ts_init,
Self::PoolFeeCollect(collect) => collect.ts_init,
Self::PoolFeeProtocolUpdate(update) => update.ts_init,
Self::PoolFeeProtocolCollect(collect) => collect.ts_init,
Self::PoolFlash(flash) => flash.ts_init,
}
}
/// Returns the instrument ID associated with this DeFi data.
///
/// # Panics
///
/// Panics if the variant is a `Block` or `PoolSnapshot` where instrument IDs are not applicable.
#[must_use]
pub fn instrument_id(&self) -> InstrumentId {
match self {
Self::Block(_) => panic!("`InstrumentId` not applicable to `Block`"), // TBD?
Self::PoolSnapshot(snapshot) => snapshot.instrument_id,
Self::PoolSwap(swap) => swap.instrument_id,
Self::PoolLiquidityUpdate(update) => update.instrument_id,
Self::PoolFeeCollect(collect) => collect.instrument_id,
Self::PoolFeeProtocolUpdate(update) => update.instrument_id,
Self::PoolFeeProtocolCollect(collect) => collect.instrument_id,
Self::Pool(pool) => pool.instrument_id,
Self::PoolFlash(flash) => flash.instrument_id,
}
}
}
impl HasTsInit for DefiData {
fn ts_init(&self) -> UnixNanos {
self.ts_init()
}
}
View on GitHub (pinned to 18893faf8b)
Solutions
- Match on the Data variant first and only call instrument_id() for PoolSwap/PoolLiquidityUpdate/PoolFee* variants
- Skip Block and PoolSnapshot messages in code paths that need an instrument ID
- Use a helper that returns Option<InstrumentId> in your own code
Example fix
// before
let id = data.instrument_id(); // panics for Block
// after
let id = match data {
Data::Block(_) | Data::PoolSnapshot(_) => None,
_ => Some(data.instrument_id()),
}; Defensive patterns
Strategy: type-guard
Validate before calling
fn instrument_id_of(data: &Data) -> Option<InstrumentId> {
match data {
Data::Block(_) | Data::PoolSnapshot(_) => None,
_ => Some(data.instrument_id()),
}
} Type guard
fn has_instrument_id(data: &Data) -> bool {
!matches!(data, Data::Block(_) | Data::PoolSnapshot(_))
} Try / catch
let id = std::panic::catch_unwind(|| data.instrument_id()).ok();
Prevention
- Match on the Data variant before accessing instrument_id
- Keep block/pool-snapshot handlers separate from pool-event handlers
- Use your own Option-returning wrapper in shared routing code
When it happens
Trigger: Calling data.instrument_id() on a Data::Block(_) or Data::PoolSnapshot(_) value, typically in generic code that processes every message from a DEX subscription identically.
Common situations: Generic data handlers/routers that dispatch on instrument_id without filtering by data type; test harnesses iterating all Data variants; subscribing to block streams and reusing pool-event handling code.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Native currency not specified for chain {}
- Must have the `chain` field set
- Invalid factory address for DEX {name} on chain {chain} for
- Liquidity addition overflow: x={current}, y={y}, delta={delt
- Liquidity subtraction underflow: x={current}, y={y}, delta={
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/467d4820bd227732.
Report an issue: GitHub.