{"record":{"id":"96ff02b813c0c0f6","repo":"Hmbown/CodeWhale","slug":"dispatch-task-was-lost-err","errorCode":null,"errorMessage":"dispatch task was lost: {err}","messagePattern":"dispatch task was lost: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/tui/src/tui/ui/event_loop.rs","lineNumber":6855,"sourceCode":"\npub(crate) async fn run_prepared_dispatch(\n    app: &mut App,\n    config: &Config,\n    engine_handle: &EngineHandle,\n    prepare: UserDispatchPrepare,\n    recovery: DispatchRecovery,\n) -> Result<()> {\n    // Unit tests that intentionally omit the production completion mailbox\n    // apply the result inline. Run the owned async phase as a task just like\n    // production does so its large future is polled from a clean executor\n    // stack instead of nesting under the test helper's call chain.\n    let apply = tokio::spawn(spawned_dispatch_inner(\n        prepare,\n        recovery,\n        engine_handle.clone(),\n    ))\n    .await\n    .map_err(|err| anyhow::anyhow!(\"dispatch task was lost: {err}\"))?;\n    apply(app, engine_handle, config)\n}\n\npub(crate) async fn run_xai_device_login_from_tui(\n    terminal: &mut AppTerminal,\n    app: &mut App,\n    engine_handle: &mut EngineHandle,\n    config: &mut Config,\n) -> Result<bool> {\n    pause_terminal(\n        terminal,\n        app.use_alt_screen(),\n        app.use_mouse_capture,\n        app.use_bracketed_paste,\n    )?;\n    let login_result = crate::oauth::login(crate::oauth::OAuthProvider::Xai).await;\n    resume_terminal(\n        terminal,","sourceCodeStart":6837,"sourceCodeEnd":6873,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/tui/ui/event_loop.rs#L6837-L6873","documentation":"run_prepared_dispatch spawns spawned_dispatch_inner via tokio::spawn and awaits the JoinHandle. If the join fails, the spawned dispatch task panicked, was aborted, or the runtime shut down before it finished — the JoinError is wrapped as \"dispatch task was lost: {err}\". The user's message dispatch never reached its apply step, so the turn did not start.","triggerScenarios":"Awaiting the JoinHandle of spawned_dispatch_inner when the task panics (bug inside dispatch application), is aborted (shutdown/cancel), or the tokio runtime terminates mid-dispatch.","commonSituations":"Quitting the TUI while a send is being prepared; a panic in the dispatch apply code path; runtime teardown races during rapid quit-after-submit; recovered dispatches whose recovery closure itself panics.","solutions":["Retry the send — the message was not applied; check whether it returned to the queue/composer.","Read the inner {err}: a panic payload points at a bug in spawned_dispatch_inner or the apply closure to fix.","Avoid quitting or cancelling the runtime during an in-flight dispatch; wait for the turn to start.","Wrap the dispatch task body in a panic boundary (catch_unwind / spawn with supervision) if the engine should survive dispatch panics."],"exampleFix":"// before\n.map_err(|err| anyhow::anyhow!(\"dispatch task was lost: {err}\"))?;\n// after\n.map_err(|err| {\n    if err.is_cancelled() {\n        anyhow::anyhow!(\"dispatch cancelled before starting; message returned to queue\")\n    } else {\n        anyhow::anyhow!(\"dispatch task was lost: {err}\")\n    }\n})?;","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match run_prepared_dispatch(terminal, app, engine_handle, config, prepare, recovery).await {\n    Ok(()) => {},\n    Err(err) if err.to_string().starts_with(\"dispatch task was lost\") => {\n        requeue_message(app, message); // let the user retry the send\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Don't quit the TUI while a send is in flight; wait for the turn to start or explicitly cancel first.","Fix panics inside spawned_dispatch_inner and its apply closures — a panic here always loses the dispatch.","Requeue the user's message on this error so nothing is silently dropped.","Distinguish cancelled vs panicked JoinErrors for clearer diagnostics."],"tags":["tokio","dispatch","task-join","async"],"backgroundTag":"api-request-failed","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"}