nikivdev/code · error

{} {} requires an interactive terminal (TTY); run this in a

Error message

{} {} requires an interactive terminal (TTY); run this in a terminal tab (e.g. Zed/Ghostty)

What it means

This error is thrown by ensure_provider_tty in src/ai.rs:5183 when an AI provider subcommand (such as browse or continue) that requires interactivity is run while stdin or stdout is not a TTY. Provider CLIs like Claude and Codex drive interactive pickers/REPLs, which cannot run when input/output is piped, redirected, or spawned without a terminal. The library bails early with a clear message naming the provider and action rather than letting the underlying CLI fail cryptically.

Source

Thrown at src/ai.rs:5183

    connect_event.source = Some("codex-connect".to_string());
    let _ = activity_log::append_daily_event(connect_event);
}

fn provider_name(provider: Provider) -> &'static str {
    match provider {
        Provider::Claude => "claude",
        Provider::Codex => "codex",
        Provider::Cursor => "cursor",
        Provider::All => "ai",
    }
}

fn ensure_provider_tty(provider: Provider, action: &str) -> Result<()> {
    if io::stdin().is_terminal() && io::stdout().is_terminal() {
        return Ok(());
    }

    bail!(
        "{} {} requires an interactive terminal (TTY); run this in a terminal tab (e.g. Zed/Ghostty)",
        provider_name(provider),
        action
    );
}

fn print_provider_session_listing(
    provider: Provider,
    target: &Path,
    sessions: &[AiSession],
    json: bool,
) -> Result<()> {
    if sessions.is_empty() {
        let provider_name = match provider {
            Provider::Claude => "Claude",
            Provider::Codex => "Codex",
            Provider::Cursor => "Cursor",
            Provider::All => "AI",

View on GitHub (pinned to a747e741ae)

Solutions

  1. Run the command directly in an interactive terminal tab (e.g. Zed/Ghostty terminal, not a task/output panel)
  2. Remove any pipe or output redirection so both stdin and stdout are terminals
  3. Use `ssh -t` when remoting so a pseudo-terminal is allocated
  4. If automation is the goal, use a non-interactive subcommand (e.g. `f ai codex sessions` with explicit path/query) instead of browse/continue

Example fix

// before (piped, fails)
$ f ai codex browse | head -n 5
// after (run interactively)
$ f ai codex browse
Defensive patterns

Strategy: validation

Validate before calling

use std::io::IsTerminal;
fn can_run_interactive() -> bool {
    std::io::stdin().is_terminal() && std::io::stdout().is_terminal()
}
if !can_run_interactive() {
    eprintln!("Run `f ai ... browse/continue` in a real terminal tab.");
    return;
}

Type guard

fn is_tty() -> bool {
    use std::io::IsTerminal;
    std::io::stdin().is_terminal() && std::io::stdout().is_terminal()
}

Try / catch

match ensure_provider_tty(provider, "browse") {
    Ok(()) => { /* launch picker */ }
    Err(e) => eprintln!("{e}; open a terminal tab and rerun interactively"),
}

Prevention

When it happens

Trigger: Running `f ai claude browse` or `f ai codex continue` (via ensure_provider_tty) when io::stdin().is_terminal() or io::stdout().is_terminal() returns false — e.g. piping output (`| grep`), redirecting (`> file`), running under a non-interactive CI job, or invoking from an editor task without a terminal tab.

Common situations: Running the command from a CI pipeline or cron job; invoking it from an editor-integrated task runner (Zed task, VS Code terminal panel configured as output-only); piping the picker output to jq/grep; running via SSH without `-t`; wrapping in scripts that capture stdout.

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/5a5a8c4b0549fcf8. Report an issue: GitHub.