warpdotdev/warp · error · anyhow::Error

Refusing to expire API key without confirmation in non-inter

Error message

Refusing to expire API key without confirmation in non-interactive mode (use --force to bypass)

What it means

Safety guard on the destructive `api-key expire` command. Without `--force` the command requires an interactive `Confirm` prompt; when stdin is not a TTY (script, pipe, CI) the prompt cannot be shown, so the command refuses to run rather than silently expiring a key.

Source

Thrown at app/src/ai/agent_sdk/api_key.rs:170

                    }
                };

                let key = match resolve_api_key_identifier(&keys, &key_identifier) {
                    Ok(Some(key)) => key,
                    Ok(None) => {
                        ctx.terminate_app(TerminationMode::ForceTerminate, None);
                        return;
                    }
                    Err(err) => {
                        super::report_fatal_error(err, ctx);
                        return;
                    }
                };

                if !force {
                    if !io::stdin().is_terminal() {
                        super::report_fatal_error(
                            anyhow!(
                                "Refusing to expire API key without confirmation in non-interactive mode (use --force to bypass)"
                            ),
                            ctx,
                        );
                        return;
                    }

                    let prompt = format!("Expire API key '{key}'?");
                    let should_expire = match Confirm::new(&prompt)
                        .with_default(false)
                        .with_help_message("This action takes effect immediately")
                        .prompt()
                    {
                        Ok(should_expire) => should_expire,
                        Err(InquireError::OperationCanceled | InquireError::OperationInterrupted) => {
                            ctx.terminate_app(TerminationMode::ForceTerminate, None);
                            return;
                        }

View on GitHub (pinned to e72fd7aacb)

Solutions

  1. Add `--force` to the command for non-interactive contexts
  2. Run inside a real TTY (`ssh -t`, `docker run -it`, `script -e ...`) when you want the confirmation prompt
  3. For rotation automation, list keys first (`warp api-key list`) to dry-run, then expire with --force

Example fix

# before (in CI, no TTY)
warp api-key expire ci-key
# after
warp api-key expire ci-key --force
Defensive patterns

Strategy: validation

Validate before calling

use std::io::IsTerminal;

fn expire_invocation_ok(force: bool) -> Result<(), String> {
    if force || std::io::stdin().is_terminal() {
        Ok(())
    } else {
        Err("stdin is not a TTY: pass --force or run interactively".into())
    }
}

Prevention

When it happens

Trigger: Running `warp api-key expire <key>` without `--force` in an environment where `io::stdin().is_terminal()` is false: CI jobs, cron, non-tty docker exec, piped stdin, some task runners.

Common situations: Automating key rotation in CI; running the CLI over ssh without -t; piping commands together; cleanup scripts inside containers.

Related errors


AI-assisted analysis of warpdotdev/warp@e72fd7aacb (2026-08-16). Data as JSON: /api/errors/dc54789c166a5982. Report an issue: GitHub.