{"record":{"id":"a1c09d680cb2632c","repo":"Hmbown/CodeWhale","slug":"steer-settlement-task-ended-before-acknowledgement","errorCode":null,"errorMessage":"Steer settlement task ended before acknowledgement","messagePattern":"Steer settlement task ended before acknowledgement","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":10495,"sourceCode":"            // engine and durable record agree even if the API caller drops.\n            let outcome_rx = permit.send_with_outcome(prompt.clone());\n            touch_lru(&mut active.lru, thread_id);\n            queued_turn = turn.clone();\n            self.spawn_steer_settlement(turn, item, prompt, outcome_rx)\n        };\n\n        // The settler owns the verdict; this is only an observation of it.\n        // Steering a streaming turn settles in milliseconds, so the caller\n        // almost always gets the truth in-band. Behind a long tool call the\n        // engine will not look at its mailbox for minutes, and an API request\n        // must not hang that long — so the wait is bounded and the caller\n        // falls back to the honest `Queued` receipt it already has.\n        match tokio::time::timeout(STEER_SETTLE_WAIT, settle_rx).await {\n            Ok(Ok((SteerOutcome::Accepted, turn))) => Ok(turn),\n            Ok(Ok((SteerOutcome::Dropped, _))) => bail!(\n                \"Turn {turn_id} moved on before the steer reached the model; {STEER_DROPPED_REASON}\"\n            ),\n            Ok(Err(_)) => bail!(\"Steer settlement task ended before acknowledgement\"),\n            Err(_) => Ok(queued_turn),\n        }\n    }\n\n    pub async fn compact_thread(\n        &self,\n        thread_id: &str,\n        req: CompactThreadRequest,\n    ) -> Result<TurnRecord> {\n        // Compaction carries a concrete provider route just like a normal\n        // turn. Keep the same reload/admission boundary through durable engine\n        // handoff so it cannot dispatch an old credential or endpoint after a\n        // successful config reload.\n        let _config_admission = self.config_admission.read().await;\n        self.ensure_accepting_execution()?;\n        let thread = self.get_thread(thread_id).await?;\n        let engine = self.ensure_engine_loaded(&thread).await?;\n","sourceCodeStart":10477,"sourceCodeEnd":10513,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_threads.rs#L10477-L10513","documentation":"Thrown when the steer settlement task's oneshot channel closes with an error before delivering an acknowledgement — i.e. the spawned settlement task terminated (panicked, was cancelled, or its sender was dropped) without reporting either Accepted or Dropped. The caller cannot know whether the steer was delivered.","triggerScenarios":"The `settle_rx` receiver gets `Err(_)` from the oneshot channel during the STEER_SETTLE_WAIT await — the settlement task ended abnormally (panic in the task, task aborted during shutdown, sender dropped without sending).","commonSituations":"Runtime shutdown or engine teardown cancels the settlement task mid-await; a bug or panic inside the settlement task; thread engine replaced while settlement was pending.","solutions":["Retry the steer after confirming the thread's engine is healthy and the turn is still InProgress.","Check runtime logs for a panic or cancellation in the settlement task; if it is a recurring crash, fix or report the task bug.","As a safe fallback, start a new turn with the steering message rather than assuming it was delivered."],"exampleFix":"// before\nlet turn = runtime.steer(thread_id, turn_id, item).await?;\n// after\nmatch runtime.steer(thread_id, turn_id, item).await {\n    Ok(turn) => turn,\n    Err(e) if e.to_string().contains(\"settlement task ended\") => {\n        // unknown delivery: safest is a fresh turn with the same item\n        runtime.start_turn(thread_id, item).await?\n    }\n    Err(e) => return Err(e),\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match runtime.steer(thread_id, turn_id, item).await {\n    Err(e) if e.to_string().contains(\"settlement task ended\") => {\n        log::error(\"steer settlement channel closed; delivery unknown\");\n        runtime.start_turn(thread_id, item).await // safe fallback\n    }\n    other => other,\n}","preventionTips":["Monitor runtime logs for settlement task panics or cancellation during shutdown.","Avoid tearing down the runtime while steer settlements are pending.","Treat unknown steer delivery as not-delivered and resend as a new turn."],"tags":["tui","steer","task-panic","channel-closed","shutdown"],"backgroundTag":"internal-invariant-violation","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}