{"record":{"id":"c4c2220a79d5ac47","repo":"Hmbown/CodeWhale","slug":"steer-receipt-task-ended-before-acknowledgement","errorCode":null,"errorMessage":"Steer receipt task ended before acknowledgement","messagePattern":"Steer receipt task ended before acknowledgement","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":9653,"sourceCode":"                Err(error) => {\n                    let cleanup = self.store.remove_item(&item.id);\n                    return match cleanup {\n                        Ok(()) => Err(error),\n                        Err(cleanup_error) => Err(anyhow!(\n                            \"Failed to persist steer: {error}; cleanup also failed: {cleanup_error}\"\n                        )),\n                    };\n                }\n            };\n            // The reserved send has no await/failure point. From here the\n            // engine and durable record agree even if the API caller drops.\n            permit.send(prompt.clone());\n            touch_lru(&mut active.lru, thread_id);\n            self.spawn_steer_receipts(turn, item, prompt)\n        };\n        receipt_rx\n            .await\n            .map_err(|_| anyhow!(\"Steer receipt task ended before acknowledgement\"))\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\n        let client_preflight_required = {\n            let active = self.active.lock().await;","sourceCodeStart":9635,"sourceCodeEnd":9671,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/runtime_threads.rs#L9635-L9671","documentation":"After enqueueing a steering prompt, the runtime awaits a receipt channel fed by a spawned acknowledgement task (`spawn_steer_receipts`). If that task terminates (panics, early return, cancellation) without sending an acknowledgement, the receiver returns a RecvError and this anyhow error is produced. It means the steering prompt was delivered to the engine but its receipt/ack was never observed, so the caller cannot confirm the steer landed.","triggerScenarios":"Calling the steer API on an active thread when the spawned receipt task exits before `receipt_rx` resolves — e.g. the task panics, the turn ends and the task is dropped, or the task returns early on an unexpected engine state.","commonSituations":"Steering a thread while the engine is concurrently shutting down or dropping the turn; a panic inside the receipt task; race between turn completion and steer submission.","solutions":["Retry the steer after confirming the thread's turn is still active","Check logs for a panic or early return inside the spawn_steer_receipts task","Ensure the engine/thread is not being closed concurrently with the steer call"],"exampleFix":"// before\nlet receipt = receipt_rx.await.map_err(|_| anyhow!(\"Steer receipt task ended before acknowledgement\"))?;\n// after\nlet receipt = match receipt_rx.await {\n    Ok(r) => r,\n    Err(_) => return Err(anyhow!(\"steer receipt task ended before acknowledgement; is the turn still active?\")),\n};","handlingStrategy":"retry","validationCode":"// confirm the thread has an active turn before steering\nif !thread_has_active_turn(thread_id) { return Err(\"cannot steer: no active turn\"); }","typeGuard":"fn receipt_alive(rx: &ReceiptRx) -> bool { !rx.is_closed() }","tryCatchPattern":"match steer(thread_id, prompt) {\n    Err(e) if e.to_string().contains(\"Steer receipt task ended\") => retry_steer_with_backoff(thread_id, prompt, 2),\n    other => other?,\n}","preventionTips":["Only steer while the turn is confirmed active","Avoid closing/shutting down the engine concurrently with steer calls","Retry transient receipt failures with a short backoff"],"tags":["async","concurrency","steering"],"backgroundTag":"thread-interrupted","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}