{"record":{"id":"48324c590997afef","repo":"Hmbown/CodeWhale","slug":"credential-file-is-not-valid-credential-json","errorCode":null,"errorMessage":"credential file {} is not valid credential JSON","messagePattern":"credential file (.+?) is not valid credential JSON","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/oauth.rs","lineNumber":1719,"sourceCode":"    let Some(raw) = crate::external_credentials::read_codewhale_owned_to_string(path)? else {\n        return Ok(None);\n    };\n    parse_auth_file(&raw, path).map(Some)\n}\n\nfn load_owned_auth_file_from_store(\n    store: &codewhale_config::XaiOAuthCredentialStore,\n    name: &str,\n) -> Result<Option<AuthFile>> {\n    let Some(raw) = store.read_to_string(name)? else {\n        return Ok(None);\n    };\n    parse_auth_file(&raw, &store.path_for(name)?).map(Some)\n}\n\nfn parse_auth_file(raw: &str, path: &Path) -> Result<AuthFile> {\n    let value: Value = serde_json::from_str(raw).map_err(|_| {\n        anyhow::anyhow!(\n            \"credential file {} is not valid credential JSON\",\n            codewhale_config::quote_os_path(path)\n        )\n    })?;\n    let obj = value.as_object().ok_or_else(|| {\n        anyhow::anyhow!(\n            \"credential file {} must be a JSON object of entries\",\n            codewhale_config::quote_os_path(path)\n        )\n    })?;\n    let mut out = BTreeMap::new();\n    for (k, v) in obj {\n        match serde_json::from_value::<OwnedAuthEntry>(v.clone()) {\n            Ok(entry) => {\n                out.insert(k.clone(), entry);\n            }\n            Err(_) => {\n                tracing::warn!(","sourceCodeStart":1701,"sourceCodeEnd":1737,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/oauth.rs#L1701-L1737","documentation":"parse_auth_file deserializes the credential store file with serde_json before interpreting it as a map of credential entries. If the file content is not parseable JSON at all, this error names the offending path (OS-quoted). It guards against corrupt or hand-edited credential stores.","triggerScenarios":"load/store code calls parse_auth_file with file contents that serde_json::from_str rejects — truncated writes, manual edits, binary content, or a placeholder written by another tool.","commonSituations":"Editing ~/.codewhale credentials by hand and leaving invalid JSON; disk-full or crash mid-write leaving a truncated file; a sync tool (dotfiles manager) replacing the file with a template.","solutions":["Open the quoted file and fix the JSON syntax, or delete the file and re-run the provider's login command to regenerate it.","Restore the file from a backup or dotfile repo.","Re-authenticate: run the provider's relogin hint (e.g. `grok login` or `codewhale auth ...`)."],"exampleFix":"// before: credentials.json contains\n{ \"xai\": { \"access_token\": \"..., // trailing comment, invalid JSON\n// after\n{ \"xai\": { \"access_token\": \"...\" } }","handlingStrategy":"validation","validationCode":"const txt = fs.readFileSync(p, 'utf8'); try { JSON.parse(txt); } catch { fixOrDelete(p); }","typeGuard":"const isJson = (s) => { try { JSON.parse(s); return true; } catch { return false; } };","tryCatchPattern":"try { useCredentials(); } catch (e) { if (String(e).includes('not valid credential JSON')) { fs.rmSync(path); await relogin(); } }","preventionTips":["Never hand-edit credential files; use the auth commands","Validate JSON after any manual edit with a parser before running","Exclude the credentials dir from partial-sync tools or ensure atomic writes"],"tags":["oauth","json","credentials"],"backgroundTag":"json-parse-error","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}