Hmbown/CodeWhale · error · anyhow::Error
dispatch task was lost: {err}
Error message
dispatch task was lost: {err} What it means
The dispatch pipeline runs its owned async phase via tokio::spawn (spawned_dispatch_inner) and awaits the JoinHandle. A JoinError means the task never produced a result: it panicked, or the runtime shut down and cancelled it. The error wraps the JoinError text (e.g. 'task 42 panicked with …' or 'task 42 was cancelled'), so the real fault is upstream of this wrapper.
Source
Thrown at crates/tui/src/tui/ui/event_loop.rs:5509
pub(crate) async fn run_prepared_dispatch(
app: &mut App,
config: &Config,
engine_handle: &EngineHandle,
prepare: UserDispatchPrepare,
recovery: DispatchRecovery,
) -> Result<()> {
// Unit tests that intentionally omit the production completion mailbox
// apply the result inline. Run the owned async phase as a task just like
// production does so its large future is polled from a clean executor
// stack instead of nesting under the test helper's call chain.
let apply = tokio::spawn(spawned_dispatch_inner(
prepare,
recovery,
engine_handle.clone(),
))
.await
.map_err(|err| anyhow::anyhow!("dispatch task was lost: {err}"))?;
apply(app, engine_handle, config)
}
pub(crate) async fn run_xai_device_login_from_tui(
terminal: &mut AppTerminal,
app: &mut App,
engine_handle: &mut EngineHandle,
config: &mut Config,
) -> Result<bool> {
pause_terminal(
terminal,
app.use_alt_screen,
app.use_mouse_capture,
app.use_bracketed_paste,
)?;
let login_result = crate::xai_oauth::device_code_login().await;
resume_terminal(
terminal,View on GitHub (pinned to 0c42157ee5)
Solutions
- Check logs/stderr for the panic message and backtrace - the root cause is inside the dispatched action, not this wrapper
- Update to the latest build and search the issue tracker for the action you ran
- Reproduce with RUST_BACKTRACE=1 and report the backtrace
- Avoid the triggering action until a fix lands
Defensive patterns
Strategy: try-catch
Try / catch
let handle = tokio::spawn(spawned_dispatch_inner(prepare, recovery, engine_handle.clone()));
match handle.await {
Ok(apply) => apply(app, engine_handle, config),
Err(join) if join.is_panic() => {
let panic = join.into_panic();
tracing::error!("dispatch panicked: {:?}", panic);
recover_gracefully() // keep TUI alive; report the action that panicked
}
Err(join) => handle_shutdown_cancellation(join),
} Prevention
- No unwrap/expect inside action handlers - return Result instead
- Run with RUST_BACKTRACE=1 in development to capture the root panic
- Keep the dispatch task panic-free so JoinError only means shutdown cancellation
When it happens
Trigger: A panic (unwrap/expect/index out of bounds) inside dispatch preparation or an action handler running in spawned_dispatch_inner; tokio runtime shutdown aborting the task mid-flight; JoinError::is_cancelled during teardown.
Common situations: A bug in a specific action handler triggered by particular app state; tests dropping the runtime early; graceful-shutdown races cancelling in-flight dispatch.
Related errors
- Invalid MCP tool name: {prefixed_name}
- OAuth login was cancelled
- MCP SSE connect cancelled before authentication completed
- MCP SSE connect cancelled before the request completed
- SSE transport cancelled before endpoint was discovered
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/96ff02b813c0c0f6.
Report an issue: GitHub.