svenstaro/genact · error
Couldn't get terminal width. Is an interactive terminal…
Error message
Couldn't get terminal width. Is an interactive terminal attached?
What it means
get_terminal_width() queries the terminal size via terminal_size::terminal_size(), and panics when it cannot determine a width. The function assumes a TTY (or on wasm32, a fake window.xterm) is attached; when stdout/stderr is not an interactive terminal (no ioctl TIOCGWINSZ data), the library deliberately panics rather than return a guessed width. It is called from run() to lay out progress/output formatting.
Solutions
- Run the program in an interactive terminal (attach a TTY: docker run -t, ssh -t, script -qec '...')
- Redirect output only for files while keeping stderr on a TTY, or use a pty wrapper (e.g. `script -c 'myapp'`)
- Patch/wrap get_terminal_width to fall back to a default width (e.g. 80) instead of panicking when no TTY is present
- Skip interactive rendering paths in run() when !atty/is-terminal detects a non-TTY stdout
Example fix
// before
pub fn get_terminal_width() -> usize {
if let Some((width, _)) = terminal_size::terminal_size() {
width.0.into()
} else {
panic!("Couldn't get terminal width. Is an interactive terminal attached?")
}
}
// after
pub fn get_terminal_width() -> usize {
terminal_size::terminal_size()
.map(|(w, _)| w.0 as usize)
.unwrap_or(80)
} Defensive patterns
Strategy: fallback
Validate before calling
let has_tty = std::io::IsTerminal::is_terminal(&std::io::stdout());
let width: usize = if has_tty {
terminal_size::terminal_size().map(|(w, _)| w.0 as usize).unwrap_or(80)
} else {
80
}; Type guard
fn terminal_available() -> bool {
use std::io::IsTerminal;
std::io::stdout().is_terminal() && terminal_size::terminal_size().is_some()
} Try / catch
// Rust panics cannot be caught with try/catch; either pre-check with terminal_available()
// or catch the unwind:
let width = std::panic::catch_unwind(get_terminal_width)
.unwrap_or(80); Prevention
- Check std::io::stdout().is_terminal() before calling run()
- Provide a default width (80/120) when no TTY exists
- Test the program with output piped (`| cat`) and under CI before release
- Use docker run -t or script -c when a PTY is required
When it happens
Trigger: Calling run() (which calls get_terminal_width()) in an environment where terminal_size::terminal_size() returns None: piping output to a file or another program, running under CI (no TTY), running in a non-interactive ssh/exec context, or a WSL/dumb terminal that does not report size.
Common situations: CI pipelines (GitHub Actions, GitLab CI) where stdout is a pipe; `cargo run | tee log.txt`; docker run without -t; background jobs or systemd services with no controlling terminal; IDE run consoles that are not real PTYs.
Understand the failure class
Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.
AI-assisted analysis of svenstaro/genact@858be63ad2 (2026-09-08).
Data as JSON: /api/errors/63c078f1479b98d0.
Report an issue: GitHub.
Appendix: source
Thrown at src/io.rs:117
pub async fn cursor_up(n: u64) {
print(format!("\x1b[{n}A")).await;
}
// pub async fn cursor_left(n: u64) {
// print(format!("\x1b[{}D", n)).await;
// }
pub async fn erase_line() {
print("\x1b[2K\x1b[0G").await;
}
#[cfg(not(target_arch = "wasm32"))]
pub fn get_terminal_width() -> usize {
if let Some((width, _)) = terminal_size::terminal_size() {
width.0.into()
} else {
panic!("Couldn't get terminal width. Is an interactive terminal attached?")
}
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen(inline_js = "export function get_terminal_width() { return window.xterm.cols }")]
extern "C" {
pub fn get_terminal_width() -> usize;
}
View on GitHub (pinned to 858be63ad2)