{"record":{"id":"ea3e6c8ee3aca6dd","repo":"Hmbown/CodeWhale","slug":"compaction-lifecycle-task-ended-before-acknowledge","errorCode":null,"errorMessage":"Compaction lifecycle task ended before acknowledgement","messagePattern":"Compaction lifecycle task ended before acknowledgement","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":10682,"sourceCode":"                current_thread.allow_shell,\n                current_thread.trust_mode,\n                policy.auto_approve(),\n                policy.permission,\n                configured_sandbox_mode,\n            );\n            engine.send_reserved_op(permit, op);\n            touch_lru(&mut active.lru, thread_id);\n            self.spawn_claimed_turn_monitor(\n                turn.clone(),\n                None,\n                engine.clone(),\n                ClaimedTurnKind::Compaction,\n            )\n        };\n\n        acceptance_rx\n            .await\n            .map_err(|_| anyhow!(\"Compaction lifecycle task ended before acknowledgement\"))?\n            .map_err(anyhow::Error::msg)\n    }\n\n    #[cfg(test)]\n    pub fn events_since(\n        &self,\n        thread_id: &str,\n        since_seq: Option<u64>,\n    ) -> Result<Vec<RuntimeEventRecord>> {\n        self.store.events_since(thread_id, since_seq)\n    }\n\n    pub(crate) async fn events_since_async(\n        &self,\n        thread_id: &str,\n        since_seq: Option<u64>,\n    ) -> Result<Vec<RuntimeEventRecord>> {\n        // Startup recovery deliberately queues terminal receipts until an","sourceCodeStart":10664,"sourceCodeEnd":10700,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_threads.rs#L10664-L10700","documentation":"Raised when the oneshot acknowledgement channel for a compaction turn is dropped before the lifecycle task sends a value — the spawned compaction monitor ended (panicked, aborted, or exited) without accepting or rejecting the compaction. The RecvError is converted to this anyhow error so the caller fails fast instead of awaiting forever.","triggerScenarios":"Awaiting `acceptance_rx` at the end of `compact_thread` after spawning the claimed-turn monitor with `ClaimedTurnKind::Compaction`, when the sender is dropped without a message.","commonSituations":"Panic inside the compaction lifecycle task (check stderr/logs); Tokio runtime shutdown mid-compaction; engine teardown/abort racing the compaction submission.","solutions":["Retry the compaction; the store was kept consistent (turn either persisted or cleaned up).","Check logs for a panic in the compaction lifecycle task at that time.","Ensure the application is not shutting down the Tokio runtime while compactions are in flight.","Report as a bug if reproducible — the lifecycle task must always send acceptance or rejection."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match runtime.compact_thread(&thread_id, req).await {\n    Err(e) if e.to_string().contains(\"ended before acknowledgement\") => {\n        // lifecycle task died: check logs for panic, then retry compaction\n    }\n    r => r?,\n}","preventionTips":["Keep the runtime alive while compactions are in flight.","Fix lifecycle-task panics surfaced in logs.","Avoid concurrent thread unload during compaction.","Ensure sufficient memory for engine worker tasks."],"tags":["compaction","channel-closed","task-panic","async"],"backgroundTag":"unexpected-response-shape","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"}