{"record":{"id":"dc54789c166a5982","repo":"warpdotdev/warp","slug":"refusing-to-expire-api-key-without-confirmation-in","errorCode":null,"errorMessage":"Refusing to expire API key without confirmation in non-interactive mode (use --force to bypass)","messagePattern":"Refusing to expire API key without confirmation in non-interactive mode \\(use --force to bypass\\)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"app/src/ai/agent_sdk/api_key.rs","lineNumber":170,"sourceCode":"                    }\n                };\n\n                let key = match resolve_api_key_identifier(&keys, &key_identifier) {\n                    Ok(Some(key)) => key,\n                    Ok(None) => {\n                        ctx.terminate_app(TerminationMode::ForceTerminate, None);\n                        return;\n                    }\n                    Err(err) => {\n                        super::report_fatal_error(err, ctx);\n                        return;\n                    }\n                };\n\n                if !force {\n                    if !io::stdin().is_terminal() {\n                        super::report_fatal_error(\n                            anyhow!(\n                                \"Refusing to expire API key without confirmation in non-interactive mode (use --force to bypass)\"\n                            ),\n                            ctx,\n                        );\n                        return;\n                    }\n\n                    let prompt = format!(\"Expire API key '{key}'?\");\n                    let should_expire = match Confirm::new(&prompt)\n                        .with_default(false)\n                        .with_help_message(\"This action takes effect immediately\")\n                        .prompt()\n                    {\n                        Ok(should_expire) => should_expire,\n                        Err(InquireError::OperationCanceled | InquireError::OperationInterrupted) => {\n                            ctx.terminate_app(TerminationMode::ForceTerminate, None);\n                            return;\n                        }","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/warpdotdev/warp/blob/e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc/app/src/ai/agent_sdk/api_key.rs#L152-L188","documentation":"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.","triggerScenarios":"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.","commonSituations":"Automating key rotation in CI; running the CLI over ssh without -t; piping commands together; cleanup scripts inside containers.","solutions":["Add `--force` to the command for non-interactive contexts","Run inside a real TTY (`ssh -t`, `docker run -it`, `script -e ...`) when you want the confirmation prompt","For rotation automation, list keys first (`warp api-key list`) to dry-run, then expire with --force"],"exampleFix":"# before (in CI, no TTY)\nwarp api-key expire ci-key\n# after\nwarp api-key expire ci-key --force","handlingStrategy":"validation","validationCode":"use std::io::IsTerminal;\n\nfn expire_invocation_ok(force: bool) -> Result<(), String> {\n    if force || std::io::stdin().is_terminal() {\n        Ok(())\n    } else {\n        Err(\"stdin is not a TTY: pass --force or run interactively\".into())\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass --force in scripts and CI","Use ssh -t / docker -it when the confirmation prompt is wanted","List keys before expiring to make --force targets explicit"],"tags":["api-key","cli","interactive","confirmation","safety","ci"],"backgroundTag":null,"analyzedSha":"e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc","analyzedAt":"2026-08-16T08:27:25.381Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}