Hmbown/CodeWhale · error · anyhow::Error
Antigravity import requires an agy_cli grant, not {}
Error message
Antigravity import requires an agy_cli grant, not {} What it means
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.
Source
Thrown at crates/tui/src/agy_credentials.rs:81
impl AntigravityCredential {
#[must_use]
pub fn source_label(&self) -> &'static str {
match self {
Self::OwnedKey(_) => "ANTIGRAVITY_API_KEY",
Self::ProcessEnv(_) => "AGY_ADC_AUTH",
Self::ExternalFile(_) => "agy state.vscdb (read-only)",
Self::None | Self::Error(_) => "none",
}
}
}
/// Extract the `agy` OAuth token from a granted `state.vscdb`.
pub(crate) fn antigravity_oauth_token_from_grant(
grant: &ExternalCredentialReadGrant,
) -> Result<Option<String>> {
if grant.source() != ExternalCredentialSource::AgyCli {
bail!(
"Antigravity import requires an agy_cli grant, not {}",
grant.source().as_str()
);
}
let path = grant.path();
// Secure-open the exact granted path first: regular file only, no
// symlink/reparse-point leaf, size-capped before any SQLite parsing.
let mut file = crate::external_credentials::open_external_regular_file(path)?;
let mut header = [0u8; 16];
let read = file.read(&mut header).with_context(|| {
format!(
"reading SQLite header of {}",
codewhale_config::quote_os_path(path)
)
})?;
if read < 16 || header[..15] != *b"SQLite format 3" {
bail!(
"external agy credential file {} is not a SQLite database",View on GitHub (pinned to 0c42157ee5)
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.
Example fix
// before
let token = antigravity_oauth_token_from_grant(&grant)?; // bails: not an agy_cli grant
// after
if grant.source() != ExternalCredentialSource::AgyCli {
anyhow::bail!("prompt the user for a read-only agy state.vscdb grant");
}
let token = antigravity_oauth_token_from_grant(&grant)?; Defensive patterns
Strategy: type-guard
Validate before calling
if grant.source() != ExternalCredentialSource::AgyCli {
anyhow::bail!("Antigravity import needs a read-only agy state.vscdb grant; current source: {}", grant.source().as_str());
} Type guard
fn is_agy_cli_grant(grant: &ExternalCredentialReadGrant) -> bool {
grant.source() == ExternalCredentialSource::AgyCli
} Try / catch
match antigravity_oauth_token_from_grant(&grant) {
Ok(token) => token,
Err(e) if e.to_string().contains("requires an agy_cli grant") => { /* request consent, then retry */ None }
Err(e) => return Err(e),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- {} has no supported external CLI credential source
- external agy credential file {} is not a SQLite database
- external agy credential store {} exceeds the {} byte safety
- external agy credential store {} changed while being read
- agy OAuth token member `{member}` is empty
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/b8799a7ad3537149.
Report an issue: GitHub.