{"record":{"id":"862d68bc91986d23","repo":"Hmbown/CodeWhale","slug":"engine-event-channel-closed-before-turn-turn-id","errorCode":null,"errorMessage":"engine event channel closed before turn {turn_id} completed","messagePattern":"engine event channel closed before turn (.+?) completed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":11576,"sourceCode":"        loop {\n            let event = if let Some(event) = pending_event.take() {\n                Some(event)\n            } else if event_channel_closed {\n                None\n            } else {\n                let mut rx = engine.rx_event.write().await;\n                rx.recv().await\n            };\n            let Some(event) = event else {\n                if self\n                    .is_interrupt_requested(&thread_id, &turn_id)\n                    .await\n                    .unwrap_or(false)\n                {\n                    turn_status = Some(RuntimeTurnStatus::Interrupted);\n                    break;\n                }\n                bail!(\"engine event channel closed before turn {turn_id} completed\");\n            };\n\n            // SyncSession and configuration operations emit control status\n            // receipts on the same channel before SendMessage is processed.\n            // They belong to engine setup, not to the next claimed turn.\n            if !saw_turn_started\n                && matches!(\n                    &event,\n                    EngineEvent::Status { .. }\n                        | EngineEvent::McpSessionBoot { .. }\n                        | EngineEvent::SessionUpdated { .. }\n                        | EngineEvent::AgentList { .. }\n                        | EngineEvent::AgentSpawned { .. }\n                        | EngineEvent::AgentProgress { .. }\n                        | EngineEvent::AgentComplete { .. }\n                        | EngineEvent::SubAgentMailbox { .. }\n                )\n            {","sourceCodeStart":11558,"sourceCodeEnd":11594,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_threads.rs#L11558-L11594","documentation":"During a turn the worker awaits events from the engine on a channel. If the channel closes (all engine senders dropped) before the turn finished and no interrupt was observed, the turn can never complete, so the worker bails with this error. It converts a silent hang into an explicit failure.","triggerScenarios":"The engine task is dropped/panicked or its event sender is closed while awaiting the next event for turn_id, and the interrupt check (which would set RuntimeTurnStatus::Interrupted) did not fire.","commonSituations":"Engine crash or panic mid-turn; runtime shutdown while a turn is in flight; a bug dropping the engine handle prematurely; process teardown racing an active turn.","solutions":["Investigate why the engine task exited (panic logs, shutdown path) before the turn finished","Retry the turn after restarting the engine/runtime session","Check for explicit shutdown calls (drop of engine handle) racing in-flight turns and serialize them"],"exampleFix":"// before\nlet replay = thread.start_turn(turn_id).await?; // engine may already be gone\n// after\nif thread.engine_alive().await {\n    thread.start_turn(turn_id).await?;\n} else {\n    thread.restart_engine().await?;\n    thread.start_turn(turn_id).await?;\n}","handlingStrategy":"retry","validationCode":"if !engine_handle.is_alive() { engine.restart().await?; }","typeGuard":null,"tryCatchPattern":"loop {\n    match thread.run_turn(turn_id).await {\n        Err(e) if e.to_string().contains(\"engine event channel closed\") => {\n            engine.restart().await?;\n            continue; // with backoff\n        }\n        other => break other,\n    }\n}","preventionTips":["Keep a strong handle to the engine sender for the turn's lifetime","Log engine task panics so channel closure is diagnosable","Serialize shutdown with in-flight turns"],"tags":["channel","engine","async","shutdown"],"backgroundTag":"broken-pipe","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"}