Hmbown/CodeWhale · error

Codewhale TUI could not verify foreground terminal ownership

Error message

Codewhale TUI could not verify foreground terminal ownership: {os_error}

What it means

Thrown by require_foreground_terminal_owner when libc::tcgetpgrp on stdin returns a negative value, meaning the foreground process group of the controlling terminal could not be queried. The launcher refuses to continue because it cannot verify it owns the terminal, and the OS error is embedded for diagnosis. This is a safety check before claiming terminal control.

Solutions

  1. Run codew from a terminal that is actually its controlling terminal (fresh shell)
  2. If in a container/service, allocate a pty (e.g. `script -qc` or docker -t)
  3. Check the embedded OS error (errno) for the specific failure (e.g. ENOTTY, ENXIO)
Defensive patterns

Strategy: try-catch

Validate before calling

let pgid = unsafe { libc::tcgetpgrp(libc::STDIN_FILENO) };
if pgid < 0 { eprintln!("no controlling terminal: {}", std::io::Error::last_os_error()); }

Try / catch

match codew::launch() {
    Err(e) if e.to_string().contains("foreground terminal ownership") => {
        eprintln!("launch from a terminal with a controlling tty");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Running with stdin detached from a controlling terminal (no ctty); a corrupted/odd pty setup; restricted sandbox or container where tcgetpgrp fails; stdin redirected to a file or pipe so there is no terminal to query.

Common situations: Launching codew from a daemon or container without a controlling tty; unusual terminal multiplexer setups; file-descriptor juggling in wrappers.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/d73ba12b191cf12f. 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 433685b202)