nautechsystems/nautilus_trader · error

RTDS task owner was dropped

Error message

RTDS task owner was dropped

What it means

Raised when the RTDS feed's task-slot owner (the weak handle to the spawned task slots held by the connect supervisor) is gone, meaning the owning task/client was dropped. The cleanup path disconnects the WebSocket and then bails. It indicates the feed instance backing this handle no longer has live bookkeeping tasks.

Source

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

        self.ensure_reconcile_worker();
        self.reconcile_once(false).await?;

        let current_generation = self.inner.shutdown_generation.lock();
        if *current_generation != generation || self.inner.closing.load(Ordering::Acquire) {
            anyhow::bail!("RTDS connect was canceled by shutdown");
        }
        Ok(())
    }

    async fn finish_retained_tasks(&self) -> anyhow::Result<()> {
        let Some(tasks) = self.task_slots() else {
            if let Some(ws) = self.current_ws() {
                ws.notify_closed();
                ws.disconnect().await;
                self.clear_ws_if_current(&ws);
            }
            anyhow::bail!("RTDS task owner was dropped");
        };

        let mut message_slot = tasks.message.lock().await;
        if let Some(outcome) =
            finish_task(&mut message_slot, Duration::ZERO, Duration::from_secs(2)).await
        {
            match outcome {
                TaskJoinOutcome::Completed(()) | TaskJoinOutcome::Aborted => {}
                TaskJoinOutcome::Failed(error) => {
                    tasks.push_shutdown_error(format!("RTDS message loop failed: {error}"));
                }
                TaskJoinOutcome::Incomplete => {
                    tasks.push_shutdown_error(
                        "RTDS message loop did not stop after abort".to_string(),
                    );
                }
            }
        }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Stop using the feed handle once shutdown/disconnect has completed; check owner liveness before further calls.
  2. Ensure worker tasks are joined or aborted before dropping the owning client.
  3. Restructure so the handle cannot outlive the task owner (strong ownership or a lifecycle flag gate).
  4. Treat as terminal for that handle; create a fresh feed instance if the stream is still needed.

Example fix

// before
feed.finish_retained_tasks().await?; // bails if owner dropped
// after
if !feed.is_owner_alive() {
    log::debug!("RTDS feed owner dropped; skipping maintenance");
    return Ok(());
}
feed.finish_retained_tasks().await?;
Defensive patterns

Strategy: try-catch

Validate before calling

if !feed.is_owner_alive() {
    log::debug!("RTDS owner dropped; not using handle");
    return Ok(());
}

Type guard

fn feed_is_usable(feed: &PolymarketRtdsFeed) -> bool {
    feed.task_slots().is_some()
}

Try / catch

if let Err(e) = feed.finish_retained_tasks().await {
    if e.to_string().contains("task owner was dropped") {
        log::debug!("feed already torn down");
    } else {
        return Err(e);
    }
}

Prevention

When it happens

Trigger: Calling finish_retained_tasks (or any reconcile/maintenance path) after the strong owner of RtdsTaskSlots was dropped — e.g. after shutdown completed or the owning client was dropped while a weak handle is still used.

Common situations: Using a feed handle after disconnect() completed; holding a Weak/cloned handle across the owner's drop; background workers outliving the client that spawned them.

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


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