{"record":{"id":"521beccd8a9b36a8","repo":"Hmbown/CodeWhale","slug":"external-agy-credential-file-is-not-a-sqlite-da","errorCode":null,"errorMessage":"external agy credential file {} is not a SQLite database","messagePattern":"external agy credential file (.+?) is not a SQLite database","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/agy_credentials.rs","lineNumber":98,"sourceCode":"    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\",\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);","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/agy_credentials.rs#L80-L116","documentation":"The granted file was secure-opened (regular file, no symlink leaf) but its first 16 bytes do not start with the 15-byte SQLite magic `SQLite format 3`. This is a cheap format check before any SQLite parsing, so a wrong-path grant or an empty/corrupted credential store fails fast and safely.","triggerScenarios":"antigravity_oauth_token_from_grant where the granted path is 0–15 bytes long, or its header differs from `SQLite format 3` — e.g. the grant points at a JSON/log/config file, a truncated state.vscdb, or a placeholder created by tooling.","commonSituations":"Consent config names the wrong file under the agy/IDE profile directory; the IDE stores credentials in a different file after a version change; the state.vscdb was copied incompletely or is mid-recreation after a crash.","solutions":["Point the read-only grant at the real agy SQLite credential store (commonly `<profile>/state.vscdb`) and retry.","Verify the file before import: `head -c 15 <path>` should print `SQLite format 3` (or `file <path>` reports SQLite 3.x).","If the store is missing/truncated, launch the Antigravity/IDE client so it recreates state.vscdb, then re-authenticate."],"exampleFix":"// before\nlet grant = consent_for(path_join(&profile, \"state.json\")); // wrong file\nlet token = antigravity_oauth_token_from_grant(&grant)?;\n\n// after\nlet grant = consent_for(path_join(&profile, \"state.vscdb\")); // real SQLite store\nlet token = antigravity_oauth_token_from_grant(&grant)?;","handlingStrategy":"validation","validationCode":"let mut f = std::fs::File::open(grant.path())?;\nlet mut magic = [0u8; 16];\nuse std::io::Read as _;\nlet n = f.read(&mut magic)?;\nif n < 16 || magic[..15] != *b\"SQLite format 3\" {\n    anyhow::bail!(\"granted path is not a SQLite db; check the consent entry\");\n}","typeGuard":"fn looks_like_sqlite(header: &[u8]) -> bool {\n    header.len() >= 16 && &header[..15] == b\"SQLite format 3\"\n}","tryCatchPattern":"match antigravity_oauth_token_from_grant(&grant) {\n    Ok(t) => t,\n    Err(e) if e.to_string().contains(\"is not a SQLite database\") => { /* fix grant path; re-consent */ return Err(e) }\n    Err(e) => return Err(e),\n}","preventionTips":["Name the exact state.vscdb file in the consent entry instead of a directory or glob.","Smoke-test the granted path with `file` or a 16-byte header read before running a full import.","Ensure the agy client has actually created and signed into its state store before import."],"tags":["antigravity","sqlite","file-format","credentials"],"backgroundTag":"invalid-file-format","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}