nautechsystems/nautilus_trader · error
IB data startup teardown failed: {teardown_error}
Error message
IB data startup teardown failed: {teardown_error} What it means
During InteractiveBrokersDataClient::connect, if the background task that monitors IB data farm notices fails to spawn, the client tears down its session/command task groups and drops the IB client handle. If that teardown (finish_tasks) itself errors, the original spawn error is re-raised with the teardown error appended, so one failure masks another.
Source
Thrown at crates/adapters/interactive_brokers/src/data/core.rs:749
self.ib_client = Some(handle);
let data_farm_state = Arc::clone(&self.data_farm_state);
let cancellation_token = self.cancellation_token.child_token();
let clock = self.clock;
if let Err(e) = self.session_tasks.spawn(async move {
if let Err(e) =
monitor_data_farm_notices(client, data_farm_state, clock, cancellation_token).await
{
tracing::warn!("IB data farm notice monitor stopped: {e:?}");
}
}) {
self.session_tasks.begin_shutdown();
self.command_tasks.begin_shutdown();
self.ib_client = None;
if let Err(teardown_error) = self.finish_tasks().await {
return Err(anyhow::Error::new(e)
.context(format!("IB data startup teardown failed: {teardown_error}")));
}
return Err(anyhow::Error::new(e).context("failed to register IB data farm monitor"));
}
self.is_connected.store(true, Ordering::Relaxed);
let instrument_count = self.instrument_provider.count();
if instrument_count > 0 {
tracing::debug!(
"Data client connected with {} instruments in provider cache",
instrument_count
);
for instrument in self.instrument_provider.get_all() {
if let Err(e) = self.data_sender.send(DataEvent::Instrument(instrument)) {
tracing::warn!("Failed to publish startup-loaded instrument: {e}");
break;
}View on GitHub (pinned to 18893faf8b)
Solutions
- Check the teardown_error in the message to see why task cleanup failed (e.g. runtime already dropped); fix that root cause first.
- Ensure connect() is only called once per client instance; create a fresh client after any failed connect.
- Call disconnect() before re-connecting so session/command task groups are cleanly re-initialized.
- Verify the tokio runtime backing the client is alive and not being shut down while connect() is awaited.
Example fix
// before
let client = IBDataClient::new(...); client.connect().await?; client.connect().await?; // second connect fails
// after
if !client.is_connected() { client.connect().await?; } Defensive patterns
Strategy: try-catch
Validate before calling
// Rust: guard against double-connect / dead runtime
if client_is_connected { return Ok(()); }
if tokio::runtime::Handle::try_current().is_err() { return Err(anyhow!("no active runtime")); } Type guard
fn is_connected(client: &IBDataClient) -> bool { client.is_connected() } Try / catch
match client.connect().await {
Err(e) if e.to_string().contains("teardown failed") => {
// inspect chained teardown_error; rebuild a fresh client
eprintln!("connect failed: {e:#}");
}
r => r?,
} Prevention
- Never call connect() twice on the same client instance
- Always await disconnect() before reconnecting
- Keep the tokio runtime alive for the client's entire lifetime
- Log errors with {e:#} to see the full anyhow context chain
When it happens
Trigger: connect() succeeds against IB Gateway/TWS but self.session_tasks.spawn(...) returns Err (task-group already shut down or spawn capacity exhausted); then finish_tasks() during rollback also returns Err.
Common situations: Calling connect() twice without disconnect(), reusing the client after a prior failed connect left the task groups in a shutdown state, or a client whose runtime was dropped mid-connect.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- failed to register IB data farm monitor
- task slot is already occupied
- Failed to start Hyperliquid WebSocket handler task: {e}
- Failed to connect after {max_attempts} attempts
- Unknown IB security type: {value}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b029d5f0b42f1e28.
Report an issue: GitHub.