Hmbown/CodeWhale · critical

Codewhale runtime thread panicked

Error message

Codewhale runtime thread panicked: {message}

What it means

The CLI runs Codewhale work on a dedicated runtime thread and joins it. If that thread panics, the panic payload (`&str` or `String`, else "unknown panic payload") is re-caught and rethrown as this anyhow error on the main thread, preserving the panic message for the user.

Solutions

  1. Read the `{message}` suffix to identify the actual panic and fix the underlying bug at that site.
  2. Reproduce with the same input/provider and add a graceful error return instead of a panic path.
  3. Check for known panicking calls (unwrap/expect) in the module named by the panic message.

Example fix

// before
let cfg = config.expect("config must exist");
// after
let cfg = config.ok_or_else(|| anyhow!("configuration missing"))?;
Defensive patterns

Strategy: try-catch

Try / catch

match run_on_runtime_thread(...) {
    Err(e) if e.to_string().starts_with("Codewhale runtime thread panicked") => {
        eprintln!("runtime crashed: {e:#}"); // then fix the panic site named in the message
    }
    other => other?,
}

Prevention

When it happens

Trigger: Any panic (`panic!`, unwrapped `None`/`Err`, index out of bounds, assertion) inside code executing on the Codewhale runtime thread.

Common situations: A bug in a provider integration or tool handler panics while the TUI is running; the user sees this wrapper message instead of an abort.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/4cb6d858874ac809. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/lib.rs:1884

    // on the ubuntu CI leg). The fat frames are the debug poll functions of
    // the giant top-level futures (`run_async_main_inner` ~5.5 MiB, `run_tui`
    // ~3.2 MiB, `handle_view_events` ~2.1 MiB), so any small addition to them
    // re-tips a zero-margin stack. 32 MiB restores real headroom; the cost is
    // address space only, since thread stacks commit lazily.
    let runtime_thread = std::thread::Builder::new()
        .name("codewhale-main".to_string())
        .stack_size(CODEWHALE_MAIN_STACK_BYTES)
        .spawn(move || run_async_main(cli, command, plugin_discovery, plugin_registry))
        .context("Failed to start the Codewhale runtime thread")?;
    match runtime_thread.join() {
        Ok(result) => result,
        Err(payload) => {
            let message = payload
                .downcast_ref::<&str>()
                .map(|value| (*value).to_string())
                .or_else(|| payload.downcast_ref::<String>().cloned())
                .unwrap_or_else(|| "unknown panic payload".to_string());
            Err(anyhow!("Codewhale runtime thread panicked: {message}"))
        }
    }
}

fn run_async_main(
    cli: Cli,
    command: Option<Commands>,
    plugin_discovery: Arc<crate::plugins::PluginDiscoveryContext>,
    plugin_registry: Arc<crate::plugins::PluginRegistry>,
) -> Result<()> {
    build_runtime(command.as_ref())?.block_on(run_async_main_inner(
        cli,
        command,
        plugin_discovery,
        plugin_registry,
    ))
}

View on GitHub (pinned to 73e0f67d83)