{"record":{"id":"a9700f157b4f0ccc","repo":"Hmbown/CodeWhale","slug":"codewhale-tui-could-not-verify-foreground-terminal-ownership-a9700f","errorCode":null,"errorMessage":"Codewhale TUI could not verify foreground terminal ownership: {}","messagePattern":"Codewhale TUI could not verify foreground terminal ownership: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/ui/terminal.rs","lineNumber":128,"sourceCode":"         or `codewhale` there — not from a pipe, cron job, or non-TTY launcher.\\n\\\n         For headless prompts use `codewhale exec \\\"…\\\"` instead.\"\n    ))\n}\n\n/// Refuse to enter terminal modes from a background Unix process group.\n///\n/// A TTY can still report `isatty(3) == true` after a shell has suspended the\n/// process. Reading from that background group triggers `SIGTTIN`; enabling\n/// mouse or keyboard protocols before that stop poisons the shell with raw\n/// escape reports. Check foreground ownership before the first mode change.\n#[cfg(unix)]\npub(crate) fn require_foreground_terminal_owner() -> Result<()> {\n    // SAFETY: both calls are read-only process/terminal queries on the\n    // controlling stdin descriptor and require no borrowed memory.\n    let (terminal_pgid, process_pgid) =\n        unsafe { (libc::tcgetpgrp(libc::STDIN_FILENO), libc::getpgrp()) };\n    if terminal_pgid < 0 {\n        return Err(anyhow::anyhow!(\n            \"Codewhale TUI could not verify foreground terminal ownership: {}\",\n            io::Error::last_os_error()\n        ));\n    }\n    validate_foreground_process_group(terminal_pgid, process_pgid)\n}\n\n#[cfg(not(unix))]\npub(crate) fn require_foreground_terminal_owner() -> Result<()> {\n    Ok(())\n}\n\n#[cfg(unix)]\npub(crate) fn validate_foreground_process_group(\n    terminal_pgid: libc::pid_t,\n    process_pgid: libc::pid_t,\n) -> Result<()> {\n    if terminal_pgid == process_pgid {","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/ui/terminal.rs#L110-L146","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\ncodew < input.txt\n// after\ncodewhale exec \"$(cat input.txt)\"","handlingStrategy":"validation","validationCode":"// Rust-ish check before launching the TUI\nlet is_tty = unsafe { libc::isatty(libc::STDIN_FILENO) == 1 };\nif !is_tty { eprintln!(\"stdin is not a terminal; use codewhale exec\"); }\nlet fg = unsafe { libc::tcgetpgrp(libc::STDIN_FILENO) };\nif fg < 0 { /* tcgetpgrp failed; no controlling terminal */ }","typeGuard":"fn has_controlling_terminal() -> bool {\n    unsafe { libc::tcgetpgrp(libc::STDIN_FILENO) >= 0 }\n}","tryCatchPattern":"match require_foreground_terminal_owner() {\n    Err(e) if e.to_string().contains(\"could not verify foreground terminal ownership\") => {\n        eprintln!(\"no controlling terminal; run in an interactive tty or use `codewhale exec`\")\n    }\n    Err(e) => return Err(e),\n    Ok(()) => {}\n}","preventionTips":["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."],"tags":["terminal","process-group","tui","unix"],"backgroundTag":"unsupported-operation","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}