{"record":{"id":"9430ff6fa3f8b083","repo":"Hmbown/CodeWhale","slug":"engine-channel-closed","errorCode":null,"errorMessage":"engine channel closed","messagePattern":"engine channel closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/ui/handlers.rs","lineNumber":495,"sourceCode":"            engine_handle.send_reserved_op(\n                permit,\n                Op::RunShellCommand {\n                    command: command.to_string(),\n                    mode: app.mode,\n                    allow_shell: app.allow_shell,\n                    trust_mode: app.trust_mode,\n                    auto_approve: app_auto_approve_enabled(app),\n                    approval_mode: app.approval_mode,\n                },\n            );\n            app.status_message = Some(format!(\"Shell command submitted: {command}\"));\n        }\n        Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {\n            app.status_message =\n                Some(\"Engine busy — shell command not sent; try again\".to_string());\n        }\n        Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {\n            return Err(anyhow::anyhow!(\"engine channel closed\"));\n        }\n    }\n    Ok(true)\n}\n\nfn report_mcp_login(app: &mut App, message: String, level: StatusToastLevel) {\n    app.push_status_toast(message.clone(), level, Some(12_000));\n    add_mcp_message(app, message);\n    app.needs_redraw = true;\n}\n\nfn start_mcp_login(app: &mut App, config: &Config, name: String, scopes: Vec<String>) {\n    use crate::tui::app::{McpLoginProgress, PendingMcpLogin};\n\n    if let Some(pending) = &app.mcp_login {\n        let server = pending.server.clone();\n        report_mcp_login(\n            app,","sourceCodeStart":477,"sourceCodeEnd":513,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/ui/handlers.rs#L477-L513","documentation":"Raised when a `!`-prefixed shell command typed in the composer is forwarded to the engine over a tokio mpsc channel and the send fails with TrySendError::Closed — the receiving end (the engine task) has been dropped. Full channels produce a friendly 'Engine busy' status, but a closed channel is unrecoverable for this input, so it propagates as an anyhow error.","triggerScenarios":"handle_bang_shell_input calls try_send on the engine command channel; the engine task has exited (crashed, panicked, or the event loop shut down) so the receiver is dropped when the user submits a bang-shell command.","commonSituations":"Engine process crashed mid-session from a provider or panic error; app teardown racing with a final composer submit; a bug that closed/dropped the engine handle while the UI event loop (run_event_loop) is still alive.","solutions":["Restart the TUI session — a closed engine channel means the engine task is gone and cannot accept commands.","Check engine logs/stderr for the panic or error that killed the engine task before the submit.","Before sending, verify the engine handle is still connected and fall back to running the shell command locally instead of erroring.","If it reproduces, report with the log: a closed receiver while the event loop runs indicates an engine lifecycle bug."],"exampleFix":"// before\nErr(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {\n    return Err(anyhow::anyhow!(\"engine channel closed\"));\n}\n// after: degrade gracefully to local execution\nErr(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {\n    app.status_message =\n        Some(\"Engine unavailable — running shell command locally\".to_string());\n    run_shell_locally(&command)?;\n}","handlingStrategy":"try-catch","validationCode":"// nothing to pre-check portably; match the send result instead\nlet is_closed = matches!(\n    engine_tx.try_send(cmd),\n    Err(tokio::sync::mpsc::error::TrySendError::Closed(_))\n);","typeGuard":null,"tryCatchPattern":"match engine_tx.try_send(cmd) {\n    Ok(()) => {}\n    Err(TrySendError::Full(_)) => status(\"Engine busy — try again\"),\n    Err(TrySendError::Closed(_)) => {\n        status(\"Engine unavailable — restart the session\");\n        // optionally run the shell command locally instead\n    }\n}","preventionTips":["Keep the engine task alive for the lifetime of the UI event loop; never drop the receiver while the loop runs.","Check engine health/connected state before enabling shell input in the composer.","Monitor engine task panics and restart the engine rather than leaving a closed channel.","Fall back to local shell execution when the engine channel is closed."],"tags":["tokio","mpsc","channel-closed","shell"],"backgroundTag":"channel-closed","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"}