nautechsystems/nautilus_trader · error

Failed to start RTDS message loop: {e}; startup rollback fai

Error message

Failed to start RTDS message loop: {e}; startup rollback failed: {shutdown_error}

What it means

Raised when the RTDS message loop fails to start AND the startup rollback (aborting/joining the just-started task) itself fails to complete cleanly — e.g. the aborted task did not stop within the 2-second join window ('message loop task did not stop after abort'). The combined message reports both the original start error and the rollback failure.

Source

Thrown at crates/adapters/polymarket/src/rtds.rs:970

        if let Err(e) = spawn_result {
            ws.disconnect().await;
            self.clear_ws_if_current(&ws);
            let shutdown_error = match finish_task(
                &mut message_slot,
                Duration::ZERO,
                Duration::from_secs(2),
            )
            .await
            {
                Some(TaskJoinOutcome::Failed(error)) => {
                    Some(format!("message loop task failed: {error}"))
                }
                Some(TaskJoinOutcome::Incomplete) => {
                    Some("message loop task did not stop after abort".to_string())
                }
                None | Some(TaskJoinOutcome::Completed(()) | TaskJoinOutcome::Aborted) => None,
            };
            anyhow::bail!(match shutdown_error {
                Some(shutdown_error) => format!(
                    "Failed to start RTDS message loop: {e}; startup rollback failed: \
                     {shutdown_error}"
                ),
                None => format!("Failed to start RTDS message loop: {e}"),
            });
        }

        Ok(true)
    }

    fn is_generation_open(&self, generation: u64) -> bool {
        let current_generation = self.inner.shutdown_generation.lock();
        *current_generation == generation && !self.inner.closing.load(Ordering::Acquire)
    }

    fn websocket_config(&self) -> WebSocketConfig {
        WebSocketConfig {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the inner error first — fix the root cause of the message-loop start failure (usually a panic or invalid initial state).
  2. Ensure the runtime stays alive until teardown joins complete; do not drop the runtime mid-abort.
  3. Harden the message loop against early panics by validating state before spawning.
  4. Check for blocking calls in the abort/join path that prevent the task from stopping.

Example fix

// before
anyhow::bail!(format!("Failed to start RTDS message loop: {e}")); // hides rollback outcome
// after
if let Some(rb) = shutdown_error {
    log::error!("RTDS startup rollback failed: {rb}");
}
anyhow::bail!("Failed to start RTDS message loop: {e}");
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = feed.connect().await {
    let msg = format!("{e:#}");
    if msg.contains("Failed to start RTDS message loop") {
        log::error!("RTDS startup failure (check rollback note): {msg}");
        // recreate feed; state may be partially torn down
    }
    return Err(e);
}

Prevention

When it happens

Trigger: spawn_result.is_err() (e.g. the task panics immediately or the spawn hook fails) and finish_task on the aborted message slot returns Incomplete within the 2-second join window.

Common situations: Message-loop task panicking at startup plus a hung join; the tokio runtime shutting down so the aborted task is never polled to completion; a blocked cleanup path in the message loop.

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/0420b3aab5d456ca. Report an issue: GitHub.