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
- Bring the job to the foreground: run `fg` in the shell before interacting.
- Launch `codew` in a fresh terminal window/tab.
- Use `codewhale exec "…"` for automated/non-interactive prompts.
- 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
- Do not launch the TUI with `&`; wait for the foreground job.
- After Ctrl-Z, run `fg` before issuing further commands that relaunch the TUI.
- Use codewhale exec for any non-interactive invocation.
- When spawning the TUI programmatically, give the child the terminal foreground pgroup (tcsetpgrp) or its own pty.
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
- Codewhale TUI could not verify foreground terminal ownership
- Codewhale TUI could not verify foreground terminal ownership
- editor handoff cancelled by pending terminal input
- terminal input pump did not pause before launching editor
- terminal input pump disconnected
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)