{"record":{"id":"b8799a7ad3537149","repo":"Hmbown/CodeWhale","slug":"antigravity-import-requires-an-agy-cli-grant-not","errorCode":null,"errorMessage":"Antigravity import requires an agy_cli grant, not {}","messagePattern":"Antigravity import requires an agy_cli grant, not (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/agy_credentials.rs","lineNumber":81,"sourceCode":"\nimpl AntigravityCredential {\n    #[must_use]\n    pub fn source_label(&self) -> &'static str {\n        match self {\n            Self::OwnedKey(_) => \"ANTIGRAVITY_API_KEY\",\n            Self::ProcessEnv(_) => \"AGY_ADC_AUTH\",\n            Self::ExternalFile(_) => \"agy state.vscdb (read-only)\",\n            Self::None | Self::Error(_) => \"none\",\n        }\n    }\n}\n\n/// Extract the `agy` OAuth token from a granted `state.vscdb`.\npub(crate) fn antigravity_oauth_token_from_grant(\n    grant: &ExternalCredentialReadGrant,\n) -> Result<Option<String>> {\n    if grant.source() != ExternalCredentialSource::AgyCli {\n        bail!(\n            \"Antigravity import requires an agy_cli grant, not {}\",\n            grant.source().as_str()\n        );\n    }\n    let path = grant.path();\n    // Secure-open the exact granted path first: regular file only, no\n    // symlink/reparse-point leaf, size-capped before any SQLite parsing.\n    let mut file = crate::external_credentials::open_external_regular_file(path)?;\n    let mut header = [0u8; 16];\n    let read = file.read(&mut header).with_context(|| {\n        format!(\n            \"reading SQLite header of {}\",\n            codewhale_config::quote_os_path(path)\n        )\n    })?;\n    if read < 16 || header[..15] != *b\"SQLite format 3\" {\n        bail!(\n            \"external agy credential file {} is not a SQLite database\",","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/agy_credentials.rs#L63-L99","documentation":"antigravity_oauth_token_from_grant in crates/tui/src/agy_credentials.rs refuses to read Antigravity credentials unless the ExternalCredentialReadGrant's source is exactly ExternalCredentialSource::AgyCli. Grants for other sources (process-env AGY_ADC_AUTH, generic external files, none) name different credential stores, so reading an agy OAuth token from them is a programming/consent mistake and is rejected up front.","triggerScenarios":"Calling antigravity_oauth_token_from_grant(&grant) where grant.source() returns ExternalCredentialSource::ProcessEnv, ExternalCredentialSource::ExternalFile for a non-agy path, or None — e.g. the import flow was handed the ambient AGY_ADC_AUTH env grant or no grant at all.","commonSituations":"Wiring the wrong consent grant into the Antigravity import command; a CLI flag that resolves the default env-based credential instead of requiring an explicit read-only grant on the agy `state.vscdb`; version changes that split grant sources into distinct enum variants.","solutions":["Build the grant from an explicit read-only consent entry (ExternalCredentialConsentToml::read_only) whose source resolves to agy_cli, pointing at the agy `state.vscdb`.","Guard before calling: `if grant.source() != ExternalCredentialSource::AgyCli { /* prompt for consent instead */ }`.","If you meant to use an env token, use the process-env credential path — not this function."],"exampleFix":"// before\nlet token = antigravity_oauth_token_from_grant(&grant)?; // bails: not an agy_cli grant\n\n// after\nif grant.source() != ExternalCredentialSource::AgyCli {\n    anyhow::bail!(\"prompt the user for a read-only agy state.vscdb grant\");\n}\nlet token = antigravity_oauth_token_from_grant(&grant)?;","handlingStrategy":"type-guard","validationCode":"if grant.source() != ExternalCredentialSource::AgyCli {\n    anyhow::bail!(\"Antigravity import needs a read-only agy state.vscdb grant; current source: {}\", grant.source().as_str());\n}","typeGuard":"fn is_agy_cli_grant(grant: &ExternalCredentialReadGrant) -> bool {\n    grant.source() == ExternalCredentialSource::AgyCli\n}","tryCatchPattern":"match antigravity_oauth_token_from_grant(&grant) {\n    Ok(token) => token,\n    Err(e) if e.to_string().contains(\"requires an agy_cli grant\") => { /* request consent, then retry */ None }\n    Err(e) => return Err(e),\n}","preventionTips":["Resolve grant source once at command startup and route to the matching credential reader.","Require an explicit read-only consent entry for file-backed credentials; never fall back to ambient env grants for Antigravity import.","Log grant.source().as_str() in diagnostics (never the token) when wiring imports."],"tags":["antigravity","credentials","consent-grant","import"],"backgroundTag":"credential-grant-mismatch","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}