Hmbown/CodeWhale · error
Codewhale TUI could not verify foreground terminal ownership
Error message
Codewhale TUI could not verify foreground terminal ownership: {} What it means
require_foreground_terminal_owner calls tcgetpgrp(STDIN_FILENO) to learn which process group owns the controlling terminal. A negative return means the tcgetpgrp syscall itself failed (stdin is not a controlling terminal or was closed), and the error embeds the errno via io::Error::last_os_error(). The TUI refuses to start because it cannot verify it is safe to take over raw terminal mode.
Solutions
- Run the TUI in a real interactive terminal with stdin attached to a tty.
- Allocate a pty if in a sandbox/automation context: `script -qec "codew" /dev/null` or use a terminal multiplexer (tmux/screen).
- For non-interactive use, switch to `codewhale exec "…"` which does not require foreground ownership.
- Check that the fd 0 is not redirected or closed in the launch wrapper; fix the launcher script.
Example fix
// before codew < input.txt // after codewhale exec "$(cat input.txt)"
Defensive patterns
Strategy: validation
Validate before calling
// Rust-ish check before launching the TUI
let is_tty = unsafe { libc::isatty(libc::STDIN_FILENO) == 1 };
if !is_tty { eprintln!("stdin is not a terminal; use codewhale exec"); }
let fg = unsafe { libc::tcgetpgrp(libc::STDIN_FILENO) };
if fg < 0 { /* tcgetpgrp failed; no controlling terminal */ } Type guard
fn has_controlling_terminal() -> bool {
unsafe { libc::tcgetpgrp(libc::STDIN_FILENO) >= 0 }
} Try / catch
match require_foreground_terminal_owner() {
Err(e) if e.to_string().contains("could not verify foreground terminal ownership") => {
eprintln!("no controlling terminal; run in an interactive tty or use `codewhale exec`")
}
Err(e) => return Err(e),
Ok(()) => {}
} Prevention
- Never redirect or close stdin when launching the TUI.
- Use codewhale exec for scripted/non-interactive calls.
- In automation, allocate a pty (script/tmux/expect) instead of a bare pipe.
- Check isatty(STDIN) before launching interactive tools from scripts.
When it happens
Trigger: Running the TUI with stdin not attached to a terminal, e.g. stdin redirected from a file/pipe (`codew < file`), stdin closed (`codew 0<&-`), stdin connected to a pty whose controlling session has been revoked, or inside environments without a real controlling tty (some CI runners, detached daemons).
Common situations: Developers piping input into the TUI, running it under process supervisors or service managers that detach the controlling tty, or in CI sandboxes where no pty is allocated. Also appears when the tty is hung up mid-launch.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Codewhale TUI cannot start from a background or suspended…
- 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/a9700f157b4f0ccc.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/ui/terminal.rs:128
or `codewhale` there — not from a pipe, cron job, or non-TTY launcher.\n\
For headless prompts use `codewhale exec \"…\"` instead."
))
}
/// Refuse to enter terminal modes from a background Unix process group.
///
/// A TTY can still report `isatty(3) == true` after a shell has suspended the
/// process. Reading from that background group triggers `SIGTTIN`; enabling
/// mouse or keyboard protocols before that stop poisons the shell with raw
/// escape reports. Check foreground ownership before the first mode change.
#[cfg(unix)]
pub(crate) fn require_foreground_terminal_owner() -> Result<()> {
// SAFETY: both calls are read-only process/terminal queries on the
// controlling stdin descriptor and require no borrowed memory.
let (terminal_pgid, process_pgid) =
unsafe { (libc::tcgetpgrp(libc::STDIN_FILENO), libc::getpgrp()) };
if terminal_pgid < 0 {
return Err(anyhow::anyhow!(
"Codewhale TUI could not verify foreground terminal ownership: {}",
io::Error::last_os_error()
));
}
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 {View on GitHub (pinned to 73e0f67d83)