{"record":{"id":"8411aa0d39d8c08a","repo":"warpdotdev/warp","slug":"expiration-behavior-is-required","errorCode":null,"errorMessage":"expiration behavior is required","messagePattern":"expiration behavior is required","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"app/src/ai/agent_sdk/api_key.rs","lineNumber":425,"sourceCode":"    }\n}\n\nfn expires_at_from_args(args: ApiKeyExpirationArgs) -> Result<Option<Time>> {\n    if args.no_expiration {\n        return Ok(None);\n    }\n\n    if let Some(expires_at) = args.expires_at {\n        return Ok(Some(Time::from(expires_at)));\n    }\n\n    if let Some(expires_in) = args.expires_in {\n        let duration = chrono::Duration::from_std(expires_in.into())\n            .map_err(|_| anyhow!(\"expiration duration is too large\"))?;\n        return Ok(Some(Time::from(Utc::now() + duration)));\n    }\n\n    Err(anyhow!(\"expiration behavior is required\"))\n}\n\nfn print_created_api_key(\n    result: CreatedApiKeyInfo,\n    output_format: OutputFormat,\n    json_output: warp_cli::json_filter::JsonOutput,\n) -> Result<()> {\n    if json_output.force_json_output() {\n        output::print_raw_json(serde_json::to_value(&result)?, &json_output)?;\n        return Ok(());\n    }\n    match output_format {\n        OutputFormat::Json => output::write_json(&result, std::io::stdout())?,\n        OutputFormat::Ndjson => output::write_json_line(&result, std::io::stdout())?,\n        OutputFormat::Pretty | OutputFormat::Text => {\n            println!(\"API key '{}' created.\", result.api_key.name);\n            println!(\"UID: {}\", result.api_key.uid);\n            println!(\"Raw API key: {}\", result.raw_api_key);","sourceCodeStart":407,"sourceCodeEnd":443,"githubUrl":"https://github.com/warpdotdev/warp/blob/e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc/app/src/ai/agent_sdk/api_key.rs#L407-L443","documentation":"`expires_at_from_args` requires exactly one expiration behavior: `--no-expiration`, `--expires-at`, or `--expires-in`. If none is provided, the function falls through to this error — the server needs an explicit expiration decision and the CLI applies no default.","triggerScenarios":"Running `warp api-key create <name>` with none of --no-expiration / --expires-at / --expires-in supplied, so the argument struct has all three unset.","commonSituations":"Wrappers that only forward name and scope flags; docs or templates written for an older CLI that defaulted to no expiration; interactive walkthroughs missing the new required flag.","solutions":["Add `--no-expiration` if the key should not expire","Add `--expires-at <timestamp>` or `--expires-in <duration>` for bounded keys","Update wrapper scripts and templates to always set exactly one expiration flag"],"exampleFix":"# before\nwarp api-key create ci\n# after\nwarp api-key create ci --expires-in 720h","handlingStrategy":"validation","validationCode":"fn expiration_specified(a: &ApiKeyExpirationArgs) -> bool {\n    a.no_expiration || a.expires_at.is_some() || a.expires_in.is_some()\n}\n// Require this to be true before invoking the create command.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Make one expiration flag mandatory in wrapper scripts","Fail fast in tooling when none is set, with a clearer message"],"tags":["api-key","arguments","validation","cli"],"backgroundTag":null,"analyzedSha":"e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc","analyzedAt":"2026-08-16T08:27:25.381Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}