{"record":{"id":"a24281c1d4393ade","repo":"xai-org/grok-build","slug":"no-oidc-auth-entry-found-in-run-grok-login-f","errorCode":null,"errorMessage":"no OIDC auth entry found in {}. Run `grok login` first.","messagePattern":"no OIDC auth entry found in (.+?)\\. Run `grok login` first\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-workspace/src/hub_auth/mod.rs","lineNumber":123,"sourceCode":"    }\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}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum OidcProviderKind {\n    Sdk,\n    Proactive,\n}\n\n/// Writes `auth.json` on the calling thread. The proactive provider already\n/// offloads this onto its seq-guarded persist worker; a nested spawn here\n/// would run `write_refreshed_token` *after* the seq check and reopen the\n/// stale-clobber race.\npub(crate) fn persist_on_refresh(auth_path: PathBuf, scope_key: String) -> OnRefreshCallback {\n    Arc::new(move |event: &RefreshEvent| {","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-workspace/src/hub_auth/mod.rs#L105-L141","documentation":"`read_auth_entry` parses `~/.grok/auth.json` (a BTreeMap of scope-key to AuthEntry) and filters for entries that have both a `refresh_token` and an `oidc_issuer`, picking the one with the latest `expires_at`. This error is thrown when the file exists and parses fine, but none of its entries qualify as OIDC-refreshable. It means the credentials present cannot be used for OIDC token refresh and the user must re-authenticate.","triggerScenarios":"Calling any code path that builds a hub auth provider (via `read_auth_entry`) when auth.json contains only entries missing `refresh_token` or `oidc_issuer` (both are `Option` with `#[serde(default)]`, so they may be absent), or when the JSON object is empty `{}`.","commonSituations":"A stale or hand-written auth.json produced by an older CLI version before OIDC fields existed; a partially-written file after a failed login; an entry created by API-key-only auth that never had a refresh token; `GROK_HOME`/`HOME` pointing at a directory with a wrong or empty auth.json.","solutions":["Run `grok login` to regenerate auth.json with a full OIDC entry (refresh_token + oidc_issuer).","Inspect the auth.json at the printed path and confirm at least one entry has non-null `refresh_token` and `oidc_issuer` fields.","Verify `GROK_HOME`/`HOME` env vars point at the profile you actually logged in with.","If the file came from an old CLI version, upgrade the CLI and log in again so the new schema fields are written."],"exampleFix":"// before: auth.json with only API-key entries\n{ \"default\": { \"key\": \"sk-...\" } }\n// after: re-login produces an OIDC-refreshable entry\n{ \"default\": { \"key\": \"sk-...\", \"refresh_token\": \"rt_...\", \"oidc_issuer\": \"https://issuer.example.com\", \"oidc_client_id\": \"client\", \"expires_at\": \"2026-09-01T00:00:00Z\" } }","handlingStrategy":"validation","validationCode":"// Pre-check auth.json before building the provider\nlet content = std::fs::read_to_string(&auth_path)?;\nlet entries: BTreeMap<String, serde_json::Value> = serde_json::from_str(&content)?;\nlet has_oidc = entries.values().any(|v| {\n    v.get(\"refresh_token\").is_some() && v.get(\"oidc_issuer\").is_some()\n});\nif !has_oidc {\n    eprintln!(\"no OIDC entry in {} — run `grok login` first\", auth_path.display());\n}","typeGuard":"fn is_oidc_entry(v: &serde_json::Value) -> bool {\n    v.get(\"refresh_token\").map_or(false, |t| t.is_string())\n        && v.get(\"oidc_issuer\").map_or(false, |i| i.is_string())\n}","tryCatchPattern":"match read_auth_entry(&auth_path) {\n    Ok((scope_key, entry)) => build_provider(&scope_key, &entry)?,\n    Err(e) if e.to_string().contains(\"no OIDC auth entry\") => {\n        prompt_grok_login()?; // recoverable: re-authenticate\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always provision credentials via `grok login`, never hand-write auth.json.","When copying profiles between machines, copy the whole auth.json, not individual entries.","Check GROK_HOME/HOME resolve to the profile you authenticated with.","Validate auth.json schema after CLI upgrades that add OIDC fields."],"tags":["auth","oidc","config","rust"],"backgroundTag":"missing-oidc-credentials","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}