From b3a40a1e7943f5bf6409e5db17c3eb6cbeee3634 Mon Sep 17 00:00:00 2001 From: Andrew Barnes Date: Thu, 5 Mar 2026 12:31:06 -0500 Subject: [PATCH] fix: log token cache errors instead of silently swallowing them 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. --- .changeset/fix-token-storage-error-logging.md | 13 +++++++ src/token_storage.rs | 38 +++++++++++++++---- 2 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 .changeset/fix-token-storage-error-logging.md diff --git a/.changeset/fix-token-storage-error-logging.md b/.changeset/fix-token-storage-error-logging.md new file mode 100644 index 0000000..dd1f5e6 --- /dev/null +++ b/.changeset/fix-token-storage-error-logging.md @@ -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. diff --git a/src/token_storage.rs b/src/token_storage.rs index 4b24ec4..c54f5f7 100644 --- a/src/token_storage.rs +++ b/src/token_storage.rs @@ -35,16 +35,38 @@ impl EncryptedTokenStorage { } async fn load_from_disk(&self) -> HashMap { - 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) -> anyhow::Result<()> {