Hmbown/CodeWhale · error
Codewhale TUI requires an interactive terminal (stdin and…
Error message
Codewhale TUI requires an interactive terminal (stdin and stdout must be a TTY). Open a real terminal (Terminal.app, iTerm, Windows Terminal, …) and run `codew` or `codewhale` there — not from a pipe, cron job, or non-TTY launcher. For headless prompts use `codewhale exec "…"` instead.
What it means
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.
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.
Example fix
# before (fails: stdout piped) codew | tee session.log # after codew # interactive TUI in a real terminal codewhale exec "summarize the diff" > session.log # headless alternative
Defensive patterns
Strategy: validation
Validate before calling
// check before launching the TUI
fn tui_can_run() -> bool {
std::io::stdin().is_terminal() && std::io::stdout().is_terminal()
}
if !tui_can_run() {
eprintln!("use `codewhale exec \"…\"` for headless runs");
std::process::exit(2);
} Try / catch
match run_tui().await {
Err(e) if e.to_string().contains("interactive terminal") => {
eprintln!("{e:#}"); // print guidance and exit non-zero
std::process::exit(2);
}
Err(e) => return Err(e),
Ok(()) => {},
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: `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.
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 could not verify foreground terminal ownership
- dsh is offline
- Failed to enable raw mode
- interactive key entry requires a terminal; use…
- OSC 52 clipboard fallback requires a terminal
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/71ce44201494de58.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/ui/terminal.rs:107
}
fn observe_terminal_attention(event: &Event) {
match event {
Event::FocusGained => crate::tui::notifications::set_terminal_focused(true),
Event::FocusLost => crate::tui::notifications::set_terminal_focused(false),
_ => {}
}
}
/// Refuse to enter raw mode unless both interactive streams are TTYs.
///
/// Keeping this check independent from `std::io` makes the launch contract
/// testable without trying to manipulate the test runner's own terminal.
pub(crate) fn require_interactive_terminal(stdin_is_tty: bool, stdout_is_tty: bool) -> Result<()> {
if stdin_is_tty && stdout_is_tty {
return Ok(());
}
Err(anyhow::anyhow!(
"Codewhale TUI requires an interactive terminal (stdin and stdout must be a TTY).\n\
Open a real terminal (Terminal.app, iTerm, Windows Terminal, …) and run `codew` \
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) =View on GitHub (pinned to 73e0f67d83)