{"record":{"id":"ab4ec8b031359c15","repo":"nautechsystems/nautilus_trader","slug":"failed-to-terminate-owner-tasks-e","errorCode":null,"errorMessage":"Failed to terminate {owner} tasks: {e}","messagePattern":"Failed to terminate (.+?) tasks: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/okx/src/common/task.rs","lineNumber":50,"sourceCode":"        tokio::select! {\n            biased;\n            () = cancel.cancelled() => {}\n            () = fut => {}\n        }\n    }) {\n        log::debug!(\"Skipping task spawn after OKX shutdown began: {e}\");\n    }\n}\n\n/// Gracefully completes and then aborts every task retained by `tasks`.\n///\n/// The scope remains closed if any handle outlives the forced completion bound.\npub(crate) async fn terminate_tasks(tasks: &TaskGroup, owner: &str) -> anyhow::Result<()> {\n    tasks.begin_shutdown();\n    tasks\n        .finish_shutdown(TASK_GRACEFUL_TIMEOUT, TASK_ABORT_TIMEOUT)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to terminate {owner} tasks: {e}\"))?;\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::{\n        Arc,\n        atomic::{AtomicBool, Ordering},\n    };\n\n    use super::*;\n\n    #[tokio::test]\n    async fn termination_cancels_registered_task() {\n        let tasks = TaskGroup::new();\n        let spawner = tasks.spawner().expect(\"task spawner\");\n        let canceled = Arc::new(AtomicBool::new(false));\n        let signal = DropSignal(Arc::clone(&canceled));","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/okx/src/common/task.rs#L32-L68","documentation":"terminate_tasks asks the shared TaskGroup to shut down its spawned tasks within a graceful timeout, then an abort timeout. If finish_shutdown returns an error (tasks ignored cancellation or hung past the abort bound), it is wrapped as \"Failed to terminate {owner} tasks\". It indicates owned background tasks (instrument refresh, data tasks) did not stop cleanly during teardown/reset.","triggerScenarios":"Calling terminate_tasks during client stop/reset/teardown when a registered task is blocked (e.g. awaiting a network call that ignores the shutdown signal, a long poll, or a deadlock) and exceeds TASK_GRACEFUL_TIMEOUT then TASK_ABORT_TIMEOUT.","commonSituations":"Live OKX adapter shutdown while a WebSocket reconnect loop or HTTP instrument refresh is in flight; a stuck request to OKX during network outage; test teardown with a task that never yields.","solutions":["Read the inner error from the message to see whether tasks timed out gracefully or at the abort bound.","Ensure spawned tasks honor cancellation (select on the shutdown signal / tokio cancellation tokens) rather than blocking awaits.","Increase TASK_GRACEFUL_TIMEOUT/TASK_ABORT_TIMEOUT only if tasks legitimately need longer (e.g. slow network); prefer making tasks cancellable.","Check for deadlocks or unbounded loops in the task body; the scope cannot close if a handle outlives the abort bound.","Log and continue on teardown in non-critical paths, since this occurs during shutdown anyway."],"exampleFix":"// before (task body ignores shutdown)\nloop {\n    let snapshot = fetch_instruments().await?;\n    publish(snapshot).await;\n}\n// after\nloop {\n    tokio::select! {\n        _ = shutdown.cancelled() => break,\n        snapshot = fetch_instruments() => publish(snapshot.await).await,\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match terminate_tasks(&task_group, \"OkxExecutionClient\").await {\n    Ok(()) => {}\n    Err(e) => tracing::error!(\"teardown: {e:#}\"), // continue shutdown\n}","preventionTips":["Write spawned tasks to honor cancellation via tokio::select! on the shutdown signal.","Avoid unbounded blocking/awaiting inside tasks (use timeouts on HTTP calls).","Test stop/reset paths so hung tasks surface during development, not live shutdown."],"tags":["okx","async","task-shutdown","timeout"],"backgroundTag":"request-timeout","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}