{"record":{"id":"fed7bd8ebe70c65b","repo":"xai-org/grok-build","slug":"failed-to-parse-e","errorCode":null,"errorMessage":"failed to parse {}: {e}","messagePattern":"failed to parse (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-workspace/src/hub_auth/mod.rs","lineNumber":110,"sourceCode":"/// Read the active OIDC entry and its scope key. The key is threaded to the\n/// refresh write so rotation updates exactly the entry that was read.\n///\n/// When several OIDC entries qualify, pick the **latest `expires_at`** — the\n/// entry the shell is actively refreshing. The previous first-key selection\n/// was alphabetical and could rotate a *different principal's* RT chain than\n/// the one the user's sessions use.\nfn read_auth_entry(path: &Path) -> anyhow::Result<(String, AuthEntry)> {\n    if !path.exists() {\n        anyhow::bail!(\n            \"No auth credentials found at {}. Run `grok login` first.\",\n            path.display()\n        );\n    }\n\n    let content = std::fs::read_to_string(path)\n        .map_err(|e| anyhow::anyhow!(\"failed to read {}: {e}\", path.display()))?;\n    let entries: BTreeMap<String, AuthEntry> = serde_json::from_str(&content)\n        .map_err(|e| anyhow::anyhow!(\"failed to parse {}: {e}\", path.display()))?;\n\n    entries\n        .into_iter()\n        .filter(|(_, e)| e.refresh_token.is_some() && e.oidc_issuer.is_some())\n        // Strictly-greater comparison: ties (including all-`None`) keep the\n        // first candidate in BTreeMap (alphabetical) order, so single-entry\n        // and legacy no-`expires_at` files behave exactly as before.\n        .fold(None::<(String, AuthEntry)>, |best, cand| match best {\n            Some(b) if cand.1.expires_at <= b.1.expires_at => Some(b),\n            _ => Some(cand),\n        })\n        .ok_or_else(|| {\n            anyhow::anyhow!(\n                \"no OIDC auth entry found in {}. Run `grok login` first.\",\n                path.display()\n            )\n        })\n}","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-workspace/src/hub_auth/mod.rs#L92-L128","documentation":"After reading the auth file, read_auth_entry deserializes it into a BTreeMap<String, AuthEntry> with serde_json. If the content is not valid JSON for that shape, this error wraps the path and the serde error. Note that entries lacking refresh_token/oidc_issuer are filtered out later — this error is only about structural/JSON validity.","triggerScenarios":"Calling read_auth_entry (or `provider()`) when auth.json is truncated, contains JSON that is not an object of AuthEntry maps, has wrong field types (e.g. expires_at not an RFC3339 datetime), or was hand-edited/corrupted.","commonSituations":"Manual edits to auth.json that broke JSON syntax; an interrupted `grok login` write leaving a partial file; a format change between tool versions (older schema); another tool overwriting the file with different JSON.","solutions":["Validate the file: `python3 -m json.tool ~/.grok/auth.json` (or $GROK_HOME/auth.json) and fix syntax errors.","Re-authenticate with `grok login` to regenerate a well-formed auth.json.","Check the expires_at format — must be a parseable chrono/Utc datetime (RFC3339).","Restore a backup of auth.json if the file was corrupted mid-write."],"exampleFix":"// before: hand-edited, wrong type for expires_at\n{ \"hub\": { \"refresh_token\": \"r\", \"oidc_issuer\": \"https://i\", \"expires_at\": \"tomorrow\" } }\n// after\n{ \"hub\": { \"refresh_token\": \"r\", \"oidc_issuer\": \"https://i\", \"expires_at\": \"2026-09-01T00:00:00Z\" } }","handlingStrategy":"validation","validationCode":"let path = default_auth_path()?;\nlet content = std::fs::read_to_string(&path)?;\nlet v: serde_json::Value = serde_json::from_str(&content)\n    .map_err(|e| anyhow::anyhow!(\"auth.json is not valid JSON: {e}\"))?;\nif !v.is_object() { anyhow::bail!(\"auth.json must be a JSON object of entries\"); }\nif let Some(exp) = v.pointer(\"/hub/expires_at\") {\n    if exp.as_str().and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok()).is_none() {\n        anyhow::bail!(\"expires_at must be RFC3339, got: {exp}\");\n    }\n}","typeGuard":"fn is_valid_auth_json(content: &str) -> bool {\n    serde_json::from_str::<BTreeMap<String, serde_json::Value>>(content).is_ok()\n}","tryCatchPattern":"match read_auth_entry().await {\n    Ok(entry) => entry,\n    Err(e) if e.to_string().starts_with(\"failed to parse\") => {\n        eprintln!(\"auth.json corrupted — run `grok login` to regenerate\");\n        prompt_relogin();\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Never hand-edit auth.json; regenerate it with `grok login`","Validate the file with a JSON linter after any manual inspection","Keep expires_at in RFC3339/UTC format","Ensure writes to auth.json are atomic (temp file + rename) to avoid truncation"],"tags":["auth","json","serialization","config"],"backgroundTag":"schema-validation-failed","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}