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
- Stop using the feed handle once shutdown/disconnect has completed; check owner liveness before further calls.
- Ensure worker tasks are joined or aborted before dropping the owning client.
- Restructure so the handle cannot outlive the task owner (strong ownership or a lifecycle flag gate).
- 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
- Do not use feed handles after disconnect/shutdown returns.
- Join background workers before dropping the owning client.
- Model ownership so handles cannot outlive the task owner.
- Create a new feed instance instead of reusing a torn-down one.
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
- Polymarket data shutdown failed: {}
- Polymarket RTDS task owner was dropped
- Hyperliquid WebSocket handler failed: {error}
- Hyperliquid WebSocket handler did not stop after abort
- Polymarket RTDS task shutdown failed: {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/7bff35934d64c7dd.
Report an issue: GitHub.