Hmbown/CodeWhale · error · anyhow::Error

Shell tracking hit an internal error — restart Codewhale to

Error message

Shell tracking hit an internal error — restart Codewhale to recover.

What it means

The shell-session manager's Mutex is poisoned: some thread panicked while holding the lock, so every later .lock() returns Err and shell tracking is dead in this process. Rust poisons a mutex whenever a panic unwinds through a critical section, and request_active_foreground_shell_background maps that poisoning to this message telling the user to restart, because poisoned state is not recoverable through this path.

Source

Thrown at crates/tui/src/tui/ui.rs:3079

    match request_active_foreground_shell_background(app) {
        Ok(()) => {
            app.status_message = Some("Moving current shell command to /jobs...".to_string());
        }
        Err(err) => {
            app.status_message = Some(err.to_string());
        }
    }
}

fn request_active_foreground_shell_background(app: &App) -> Result<()> {
    let shell_manager = app
        .runtime_services
        .shell_manager
        .clone()
        .context("No shell session is active.")?;
    let mut manager = shell_manager.lock().map_err(|_| {
        anyhow::anyhow!("Shell tracking hit an internal error — restart Codewhale to recover.")
    })?;
    manager.request_foreground_background();
    Ok(())
}

pub(crate) fn prefill_jobs_cancel_all_if_tasks_sidebar(app: &mut App) -> bool {
    if !app.view_stack.is_empty()
        || app.work_surface.panel != crate::tui::work_surface::RailPanel::Tasks
        || app.work_surface.last_area.is_none()
        || !app
            .task_panel
            .iter()
            .any(|task| task.id.starts_with("shell_") && task.status == "running")
    {
        return false;
    }

    app.input = "/jobs cancel-all".to_string();

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Restart the TUI as the message instructs - the poisoned lock cannot be recovered in-process
  2. Update to a newer build; the root cause is the original panic, so check the changelog/issue tracker for a fix
  3. Capture logs and the panic backtrace (RUST_BACKTRACE=1) and report it
  4. Avoid the interaction that triggered the original panic until the fix is installed
Defensive patterns

Strategy: try-catch

Try / catch

match shell_manager.lock() {
    Ok(mut manager) => { manager.request_foreground_background(); Ok(()) }
    Err(poisoned) => {
        tracing::error!("shell manager lock poisoned: {:?}", poisoned);
        // only if invariants are known re-establishable:
        // let mut manager = poisoned.into_inner();
        disable_shell_tracking_and_prompt_restart()
    }
}

Prevention

When it happens

Trigger: Calling request_active_foreground_shell_background() (backgrounding the foreground shell) after any earlier panic anywhere the shell_manager lock was held - e.g. a PTY I/O thread or command-tracking code path panicked and unwound through the locked region.

Common situations: A bug-triggering panic during shell output handling in an older build; stress scenarios killing worker threads; unwinding across lock boundaries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/b17a1609fa1f4336. Report an issue: GitHub.