{"record":{"id":"f8e3c67ac74d1212","repo":"Hmbown/CodeWhale","slug":"external-agy-credential-store-exceeds-the-by","errorCode":null,"errorMessage":"external agy credential store {} exceeds the {} byte safety limit","messagePattern":"external agy credential store (.+?) exceeds the (.+?) byte safety limit","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/agy_credentials.rs","lineNumber":108,"sourceCode":"    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\",\n            codewhale_config::quote_os_path(path)\n        );\n    }\n    file.seek(SeekFrom::Start(0)).ok();\n    let metadata = file\n        .metadata()\n        .with_context(|| format!(\"statting {}\", codewhale_config::quote_os_path(path)))?;\n    if metadata.len() > AGY_STATE_DB_LIMIT {\n        bail!(\n            \"external agy credential store {} exceeds the {} byte safety limit\",\n            codewhale_config::quote_os_path(path),\n            AGY_STATE_DB_LIMIT\n        );\n    }\n    // Pin the file identity: SQLite reopens the path by name, so hold the\n    // secure handle open across the query and prove the inode did not move.\n    let pinned = file_identity(&file);\n    drop(file);\n    let value = query_oauth_token(path)?;\n    let reopened = std::fs::File::open(path)\n        .ok()\n        .and_then(|recheck| file_identity_of(&recheck));\n    if pinned != reopened {\n        bail!(\n            \"external agy credential store {} changed while being read\",\n            codewhale_config::quote_os_path(path)\n        );","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/agy_credentials.rs#L90-L126","documentation":"After the SQLite header check, the code stats the granted file and refuses to parse it if metadata.len() exceeds AGY_STATE_DB_LIMIT. The limit is a resource-safety cap: it prevents an attacker-controlled or accidentally huge file from being fed into the SQLite parser. A legitimate state.vscdb is normally far below the cap.","triggerScenarios":"antigravity_oauth_token_from_grant on a granted file whose on-disk size is larger than AGY_STATE_DB_LIMIT — e.g. a state.vscdb that grew huge from accumulated IDE history/blob data, or a grant pointed at some other large .db file.","commonSituations":"Long-lived IDE profiles with un-vacuumed SQLite stores; grants matching a directory of databases by glob so the wrong (large) db is selected; testing against synthetic oversized fixtures.","solutions":["Have the Antigravity/IDE client compact the store (close it, then vacuum or let it prune WAL/history) so state.vscdb shrinks below AGY_STATE_DB_LIMIT.","Check `std::fs::metadata(path)?.len()` against the limit before invoking the import, and surface an actionable message.","Fix the consent entry so it names the actual credential store rather than a broad path that can match a large unrelated database."],"exampleFix":"// before\nlet token = antigravity_oauth_token_from_grant(&grant)?; // bails: exceeds safety limit\n\n// after\nconst AGY_STATE_DB_LIMIT: u64 = 64 * 1024 * 1024; // keep in sync with tui\nif std::fs::metadata(grant.path())?.len() > AGY_STATE_DB_LIMIT {\n    anyhow::bail!(\"compact the agy state.vscdb (vacuum) before import\");\n}\nlet token = antigravity_oauth_token_from_grant(&grant)?;","handlingStrategy":"validation","validationCode":"const AGY_STATE_DB_LIMIT: u64 = 64 * 1024 * 1024; // keep in sync with crates/tui\nlet len = std::fs::metadata(grant.path())?.len();\nif len > AGY_STATE_DB_LIMIT {\n    anyhow::bail!(\"state.vscdb is {len} bytes; compact (vacuum) it before import\");\n}","typeGuard":null,"tryCatchPattern":"match antigravity_oauth_token_from_grant(&grant) {\n    Ok(t) => t,\n    Err(e) if e.to_string().contains(\"safety limit\") => { /* prompt user to vacuum/close client */ return Err(e) }\n    Err(e) => return Err(e),\n}","preventionTips":["Close the Antigravity/IDE client and vacuum the profile database periodically.","Check file size before import and surface the limit number in the error UI.","Keep the consent entry scoped to the single credential db, not other large databases in the profile."],"tags":["antigravity","sqlite","size-limit","credentials"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}