{"record":{"id":"71ce44201494de58","repo":"Hmbown/CodeWhale","slug":"codewhale-tui-requires-an-interactive-terminal-st","errorCode":null,"errorMessage":"Codewhale TUI requires an interactive terminal (stdin and stdout must be a TTY).\nOpen a real terminal (Terminal.app, iTerm, Windows Terminal, …) and run `codew` or `codewhale` there — not from a pipe, cron job, or non-TTY launcher.\nFor headless prompts use `codewhale exec \"…\"` instead.","messagePattern":"Codewhale TUI requires an interactive terminal \\(stdin and stdout must be a TTY\\)\\.\nOpen a real terminal \\(Terminal\\.app, iTerm, Windows Terminal, …\\) and run `codew` or `codewhale` there — not from a pipe, cron job, or non-TTY launcher\\.\nFor headless prompts use `codewhale exec \"…\"` instead\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/ui/terminal.rs","lineNumber":107,"sourceCode":"}\n\nfn observe_terminal_attention(event: &Event) {\n    match event {\n        Event::FocusGained => crate::tui::notifications::set_terminal_focused(true),\n        Event::FocusLost => crate::tui::notifications::set_terminal_focused(false),\n        _ => {}\n    }\n}\n\n/// Refuse to enter raw mode unless both interactive streams are TTYs.\n///\n/// Keeping this check independent from `std::io` makes the launch contract\n/// testable without trying to manipulate the test runner's own terminal.\npub(crate) fn require_interactive_terminal(stdin_is_tty: bool, stdout_is_tty: bool) -> Result<()> {\n    if stdin_is_tty && stdout_is_tty {\n        return Ok(());\n    }\n    Err(anyhow::anyhow!(\n        \"Codewhale TUI requires an interactive terminal (stdin and stdout must be a TTY).\\n\\\n         Open a real terminal (Terminal.app, iTerm, Windows Terminal, …) and run `codew` \\\n         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) =","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/ui/terminal.rs#L89-L125","documentation":"Launch preflight error from require_interactive_terminal: the Codewhale TUI was started when either stdin or stdout is not a TTY, so an interactive full-screen UI cannot run. The message explains the environment problem and points to `codewhale exec \"…\"` for headless/non-interactive use. The TTY check is deliberately independent of std::io so the launch contract stays testable.","triggerScenarios":"Running `codew`/`codewhale` (run_tui -> require_interactive_terminal) with stdin_is_tty == false or stdout_is_tty == false — piping input/output, running under cron or a service manager, launching from an IDE task runner or CI, or redirecting stdout to a file.","commonSituations":"`codew < file` or `codew | tee log`; cron/systemd jobs; IDE run-configurations that capture output; SSH without `-t`; containers or exec wrappers without a pseudo-terminal.","solutions":["Run the command in a real interactive terminal (Terminal.app, iTerm, Windows Terminal) with no pipes or redirections on stdin/stdout.","For SSH, attach a TTY: `ssh -t host codew`.","For non-interactive/headless use, switch to `codewhale exec \"…\"` which is designed for pipes and scripts.","In IDEs or launchers, configure the task to run in an integrated terminal rather than capturing output."],"exampleFix":"# before (fails: stdout piped)\ncodew | tee session.log\n# after\ncodew            # interactive TUI in a real terminal\ncodewhale exec \"summarize the diff\" > session.log   # headless alternative","handlingStrategy":"validation","validationCode":"// check before launching the TUI\nfn tui_can_run() -> bool {\n    std::io::stdin().is_terminal() && std::io::stdout().is_terminal()\n}\nif !tui_can_run() {\n    eprintln!(\"use `codewhale exec \\\"…\\\"` for headless runs\");\n    std::process::exit(2);\n}","typeGuard":null,"tryCatchPattern":"match run_tui().await {\n    Err(e) if e.to_string().contains(\"interactive terminal\") => {\n        eprintln!(\"{e:#}\"); // print guidance and exit non-zero\n        std::process::exit(2);\n    }\n    Err(e) => return Err(e),\n    Ok(()) => {},\n}","preventionTips":["Check `std::io::stdin().is_terminal()` and `stdout().is_terminal()` in launch scripts before starting the TUI.","Use `codewhale exec \"…\"` for cron, CI, IDE tasks, and any piped invocation.","Request a TTY for remote runs: `ssh -t`.","Configure IDE runners to use an integrated terminal instead of capturing output."],"tags":["tty","terminal","cli","launch"],"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"}