{"record":{"id":"7bff35934d64c7dd","repo":"nautechsystems/nautilus_trader","slug":"rtds-task-owner-was-dropped","errorCode":null,"errorMessage":"RTDS task owner was dropped","messagePattern":"RTDS task owner was dropped","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/polymarket/src/rtds.rs","lineNumber":574,"sourceCode":"\n        self.ensure_reconcile_worker();\n        self.reconcile_once(false).await?;\n\n        let current_generation = self.inner.shutdown_generation.lock();\n        if *current_generation != generation || self.inner.closing.load(Ordering::Acquire) {\n            anyhow::bail!(\"RTDS connect was canceled by shutdown\");\n        }\n        Ok(())\n    }\n\n    async fn finish_retained_tasks(&self) -> anyhow::Result<()> {\n        let Some(tasks) = self.task_slots() else {\n            if let Some(ws) = self.current_ws() {\n                ws.notify_closed();\n                ws.disconnect().await;\n                self.clear_ws_if_current(&ws);\n            }\n            anyhow::bail!(\"RTDS task owner was dropped\");\n        };\n\n        let mut message_slot = tasks.message.lock().await;\n        if let Some(outcome) =\n            finish_task(&mut message_slot, Duration::ZERO, Duration::from_secs(2)).await\n        {\n            match outcome {\n                TaskJoinOutcome::Completed(()) | TaskJoinOutcome::Aborted => {}\n                TaskJoinOutcome::Failed(error) => {\n                    tasks.push_shutdown_error(format!(\"RTDS message loop failed: {error}\"));\n                }\n                TaskJoinOutcome::Incomplete => {\n                    tasks.push_shutdown_error(\n                        \"RTDS message loop did not stop after abort\".to_string(),\n                    );\n                }\n            }\n        }","sourceCodeStart":556,"sourceCodeEnd":592,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/polymarket/src/rtds.rs#L556-L592","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nfeed.finish_retained_tasks().await?; // bails if owner dropped\n// after\nif !feed.is_owner_alive() {\n    log::debug!(\"RTDS feed owner dropped; skipping maintenance\");\n    return Ok(());\n}\nfeed.finish_retained_tasks().await?;","handlingStrategy":"try-catch","validationCode":"if !feed.is_owner_alive() {\n    log::debug!(\"RTDS owner dropped; not using handle\");\n    return Ok(());\n}","typeGuard":"fn feed_is_usable(feed: &PolymarketRtdsFeed) -> bool {\n    feed.task_slots().is_some()\n}","tryCatchPattern":"if let Err(e) = feed.finish_retained_tasks().await {\n    if e.to_string().contains(\"task owner was dropped\") {\n        log::debug!(\"feed already torn down\");\n    } else {\n        return Err(e);\n    }\n}","preventionTips":["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."],"tags":["websocket","shutdown","lifecycle","polymarket","async"],"backgroundTag":"invalid-state-transition","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}