nautechsystems/nautilus_trader · error
book channel present
Error message
book channel present
What it means
`SubscriptionDispatchState::activate` records which websocket channel (e.g. `summary`, `trades`) owns a subscription. For book-delta and book-depth10 owners it expects the channel string to be present and panics otherwise. The invariant is that book-channel subscriptions are only activated after the channel assignment is known.
Source
Thrown at crates/adapters/derive/src/data.rs:1905
let mut state = self.registry.state.lock();
let removed = state.remove(owner)?;
self.dispatch.deactivate(owner, removed.channel_empty);
Some(removed)
}
fn has_owners(&self, channel: &str) -> bool {
let _guard = self.lock.lock();
self.registry.state.lock().has_owners(channel)
}
}
impl SubscriptionDispatchState {
fn activate(&self, owner: ChannelOwner, channel: Option<&str>) {
match owner {
ChannelOwner::BookDeltas(instrument_id) => {
self.active_book_delta_channels.insert(
instrument_id,
channel.expect("book channel present").to_string(),
);
}
ChannelOwner::BookDepth10(instrument_id) => {
self.active_book_depth10_channels.insert(
instrument_id,
channel.expect("book channel present").to_string(),
);
}
ChannelOwner::Ticker {
instrument_id,
feed,
} => {
self.active_ticker_channels.insert(
instrument_id,
channel.expect("ticker channel present").to_string(),
);
self.ticker_subscriptions(feed).insert(instrument_id);
}View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure the subscribe-confirmation handler stores the channel name before calling `activate` for book owners.
- On reconnect, re-derive the channel ("book" / "depth10") for book subscriptions rather than passing None.
- Change `activate` to accept `&str` for book owners (make the requirement explicit) or fall back to the default channel name instead of panicking.
Example fix
// before
self.activate(ChannelOwner::BookDeltas(instrument_id), None);
// after
self.activate(ChannelOwner::BookDeltas(instrument_id), Some("book")); Defensive patterns
Strategy: validation
Validate before calling
// before activate
if channel.is_none() && matches!(owner, ChannelOwner::BookDeltas(_) | ChannelOwner::BookDepth10(_)) {
// defer activation until subscribe confirmation arrives
return;
} Try / catch
let result = std::panic::catch_unwind(|| state.activate(owner, channel));
if result.is_err() { log_and_resubscribe(); } Prevention
- Activate book subscriptions only after the subscribe confirmation provides the channel name.
- On reconnect, re-derive the channel name for book subscriptions.
- Pass a concrete channel string for any new ChannelOwner variants.
When it happens
Trigger: Activating a `ChannelOwner::BookDeltas` or `BookDepth10` owner while the `channel` parameter is `None` — i.e. the subscription activation path is invoked before the websocket subscribe confirmation carrying the channel name arrives.
Common situations: A websocket reconnect/replay path that re-activates cached book subscriptions without re-deriving their channel names, an out-of-order message where activation precedes the subscribe ack, or a code change adding a new book owner that forgets to pass its channel.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- InstrumentState channel requires kind and currency parameter
- Close command should not be drained
- Flush command should not be drained
- Order invariant violated: first event must be OrderInitializ
- noid '{}' does not match new order oid '{}'
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/10400663cb4fe236.
Report an issue: GitHub.