Hmbown/CodeWhale · error

Codewhale TUI cannot start from a background or suspended…

Error message

Codewhale TUI cannot start from a background or suspended terminal job (terminal foreground process group {terminal_pgid}, Codewhale process group {process_pgid}).
Run `fg` to foreground the job or launch `codew` in a new terminal. For automated prompts use `codewhale exec "…"` instead.

What it means

validate_foreground_process_group rejects TUI startup when the terminal's foreground process group (from tcgetpgrp) differs from the Codewhale process group. A background/suspended job must not put the terminal into raw mode or fight the shell for input, so startup is blocked with guidance to foreground the job.

Solutions

  1. Bring the job to the foreground: run `fg` in the shell before interacting.
  2. Launch `codew` in a fresh terminal window/tab.
  3. Use `codewhale exec "…"` for automated/non-interactive prompts.
  4. If spawning programmatically, call tcsetpgrp to make the child the terminal's foreground process group, or run the child with its own pty.

Example fix

// before
$ codew &   # blocked: background job
// after
$ fg %1     # or launch codew directly in the foreground
Defensive patterns

Strategy: validation

Validate before calling

let fg = unsafe { libc::tcgetpgrp(libc::STDIN_FILENO) };
let mine = unsafe { libc::getpgrp() };
if fg >= 0 && fg != mine {
    eprintln!("background job: run `fg` first or use codewhale exec");
}

Type guard

fn is_foreground_job() -> bool {
    let fg = unsafe { libc::tcgetpgrp(libc::STDIN_FILENO) };
    fg >= 0 && fg == unsafe { libc::getpgrp() }
}

Prevention

When it happens

Trigger: Launching `codew` while the process is a background job of the shell (e.g. `codew &` then ignoring job control), resuming a suspended session (`Ctrl-Z` then running without `fg`), or starting the TUI from a scripted context where another process group holds the terminal foreground.

Common situations: Developer starts codew, hits Ctrl-Z, then launches it again from a script; or an alias/wrapper backgrounds the process; or a test harness spawns codew without granting it the foreground pgroup via tcsetpgrp.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/tui/ui/terminal.rs:149

        ));
    }
    validate_foreground_process_group(terminal_pgid, process_pgid)
}

#[cfg(not(unix))]
pub(crate) fn require_foreground_terminal_owner() -> Result<()> {
    Ok(())
}

#[cfg(unix)]
pub(crate) fn validate_foreground_process_group(
    terminal_pgid: libc::pid_t,
    process_pgid: libc::pid_t,
) -> Result<()> {
    if terminal_pgid == process_pgid {
        return Ok(());
    }
    Err(anyhow::anyhow!(
        "Codewhale TUI cannot start from a background or suspended terminal job \
         (terminal foreground process group {terminal_pgid}, Codewhale process group {process_pgid}).\n\
         Run `fg` to foreground the job or launch `codew` in a new terminal. \
         For automated prompts use `codewhale exec \"…\"` instead."
    ))
}

pub(crate) fn subagent_terminal_projection_from_mailbox(
    message: &MailboxMessage,
) -> Option<(&str, SubAgentStatus, Option<String>)> {
    match message {
        MailboxMessage::Completed { agent_id, summary } => Some((
            agent_id.as_str(),
            SubAgentStatus::Completed,
            Some(summary.clone()),
        )),
        MailboxMessage::Failed { agent_id, error } => Some((
            agent_id.as_str(),

View on GitHub (pinned to 73e0f67d83)