Hmbown/CodeWhale · error
Codex credential file
Error message
Codex credential file {} is not valid credential JSON What it means
`oauth::load_credentials` reads the stored Codex OAuth credential file and parses it as `CodexAuthFile` JSON. If the file exists but is not valid JSON (or not the expected credential shape), the parse error is converted into this message. The path is inserted via `quote_os_path` specifically so that malformed-file errors never echo raw file contents, which could leak tokens.
Solutions
- Delete the corrupt credential file and re-run the Codex OAuth sign-in flow to regenerate it
- Validate the file is parseable JSON with the expected top-level fields before relying on it
- Check for interrupted writes/backups (e.g. a `.tmp` file) and restore a complete copy
Example fix
// before: manually patched tokens.json by hand, left trailing comma
{"tokens": {"access": "...",}}
// after: re-authenticate to let the library write valid JSON
rm ~/.codex/auth.json # then run the sign-in flow
{"tokens": {"access": "...", "refresh": "..."}} Defensive patterns
Strategy: try-catch
Validate before calling
fn credentials_look_valid(path: &Path) -> bool {
std::fs::read_to_string(path)
.ok()
.and_then(|c| serde_json::from_str::<serde_json::Value>(&c).ok())
.map_or(false, |v| v.get("tokens").is_some())
} Try / catch
match oauth::get_credentials() {
Ok(creds) => creds,
Err(e) if e.to_string().contains("not valid credential JSON") => {
delete_credential_file();
start_fresh_sign_in()? // regenerate credentials
}
Err(e) => return Err(e),
} Prevention
- Never hand-edit credential files; always re-run the OAuth sign-in to change them
- Ensure token writes are atomic (write temp + rename) to avoid truncated files
- Check disk space / write completion before assuming credentials saved
- Treat 'stored_credentials_present' with the same parse check, not just file existence
When it happens
Trigger: Calling `get_credentials`/`stored_credentials_present` when the credential file at the granted path contains non-JSON data, an empty/truncated file, or JSON that does not deserialize into `CodexAuthFile` (e.g. missing `tokens`).
Common situations: A manual edit or partial write of the credential file left it corrupt; a credential file created by an older/other tool with a different schema; disk-full or crash during a previous token write left truncated JSON; the file was overwritten by unrelated content.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- agy OAuth token JSON carries no access token member
- credential file is not valid credential JSON
- agy OAuth token member
- atomically replacing xAI OAuth credentials
- bearer credentials are not an API key
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/e39fdf19a72860f2.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/oauth.rs:120
// 60-second safety margin
now + 60 >= exp
}
// If we can't prove freshness, fail closed. External credentials are
// never refreshed by Codewhale.
None => true,
}
}
/// Load Codex credentials from the auth file.
///
/// Returns `Ok(None)` if the file doesn't exist or has no usable tokens.
/// Returns `Err` only on parse/IO errors that aren't "file not found".
fn load_credentials(grant: &ExternalCredentialReadGrant) -> Result<Option<CodexCredentials>> {
let Some(contents) = crate::external_credentials::read_to_string(grant)? else {
return Ok(None);
};
let auth: CodexAuthFile = serde_json::from_str(&contents).map_err(|_| {
anyhow::anyhow!(
"Codex credential file {} is not valid credential JSON",
codewhale_config::quote_os_path(grant.path())
)
})?;
let tokens = match auth.tokens {
Some(t) => t,
None => return Ok(None),
};
let access_token = match tokens.access_token {
Some(t) if !t.trim().is_empty() => t,
_ => return Ok(None),
};
Ok(Some(CodexCredentials {
access_token,
account_id: tokens.account_id,
}))
}
View on GitHub (pinned to 73e0f67d83)