fix: log token cache errors instead of silently swallowing them (#193)

load_from_disk used four nested if-let-Ok blocks that silently
returned an empty HashMap on any failure. When the encryption key
rotated or the cache file was corrupted, tokens silently stopped
loading and users were forced to re-authenticate with no explanation.

Replace with explicit match arms that log specific warnings to
stderr for each failure mode:
- Decryption failure (key changed, corrupted data)
- Invalid UTF-8 in decrypted data
- JSON deserialization failure

File-not-found is still silent since that's normal on first run.
This commit is contained in:
Andrew Barnes
2026-03-05 17:37:29 -05:00
committed by GitHub
parent 1ff5bc820f
commit 9a780d73d4
2 changed files with 43 additions and 8 deletions
@@ -0,0 +1,13 @@
---
"@anthropic/gws": patch
---
Log token cache decryption/parse errors instead of silently swallowing
Previously, `load_from_disk` used four nested `if let Ok` blocks that
silently returned an empty map on any failure. When the encryption key
changed or the cache was corrupted, tokens silently stopped loading and
users were forced to re-authenticate with no explanation.
Now logs specific warnings to stderr for decryption failures, invalid
UTF-8, and JSON parse errors, with a hint to re-authenticate.
+30 -8
View File
@@ -35,16 +35,38 @@ impl EncryptedTokenStorage {
}
async fn load_from_disk(&self) -> HashMap<String, TokenInfo> {
if let Ok(data) = tokio::fs::read(&self.file_path).await {
if let Ok(decrypted) = crate::credential_store::decrypt(&data) {
if let Ok(json) = String::from_utf8(decrypted) {
if let Ok(map) = serde_json::from_str(&json) {
return map;
}
}
let data = match tokio::fs::read(&self.file_path).await {
Ok(d) => d,
Err(_) => return HashMap::new(), // File doesn't exist yet — normal on first run
};
let decrypted = match crate::credential_store::decrypt(&data) {
Ok(d) => d,
Err(e) => {
eprintln!(
"warning: failed to decrypt token cache ({}): {e:#}",
self.file_path.display()
);
eprintln!("hint: you may need to re-authenticate with `gws auth login`");
return HashMap::new();
}
};
let json = match String::from_utf8(decrypted) {
Ok(j) => j,
Err(e) => {
eprintln!("warning: token cache contains invalid UTF-8: {e}");
return HashMap::new();
}
};
match serde_json::from_str(&json) {
Ok(map) => map,
Err(e) => {
eprintln!("warning: failed to parse token cache JSON: {e}");
HashMap::new()
}
}
HashMap::new()
}
async fn save_to_disk(&self, map: &HashMap<String, TokenInfo>) -> anyhow::Result<()> {