xai-org/grok-build · error
no OIDC auth entry found in {}. Run `grok login` first.
Error message
no OIDC auth entry found in {}. Run `grok login` first. What it means
`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.
Source
Thrown at crates/codegen/xai-grok-workspace/src/hub_auth/mod.rs:123
}
let content = std::fs::read_to_string(path)
.map_err(|e| anyhow::anyhow!("failed to read {}: {e}", path.display()))?;
let entries: BTreeMap<String, AuthEntry> = serde_json::from_str(&content)
.map_err(|e| anyhow::anyhow!("failed to parse {}: {e}", path.display()))?;
entries
.into_iter()
.filter(|(_, e)| e.refresh_token.is_some() && e.oidc_issuer.is_some())
// Strictly-greater comparison: ties (including all-`None`) keep the
// first candidate in BTreeMap (alphabetical) order, so single-entry
// and legacy no-`expires_at` files behave exactly as before.
.fold(None::<(String, AuthEntry)>, |best, cand| match best {
Some(b) if cand.1.expires_at <= b.1.expires_at => Some(b),
_ => Some(cand),
})
.ok_or_else(|| {
anyhow::anyhow!(
"no OIDC auth entry found in {}. Run `grok login` first.",
path.display()
)
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum OidcProviderKind {
Sdk,
Proactive,
}
/// Writes `auth.json` on the calling thread. The proactive provider already
/// offloads this onto its seq-guarded persist worker; a nested spawn here
/// would run `write_refreshed_token` *after* the seq check and reopen the
/// stale-clobber race.
pub(crate) fn persist_on_refresh(auth_path: PathBuf, scope_key: String) -> OnRefreshCallback {
Arc::new(move |event: &RefreshEvent| {View on GitHub (pinned to bc7f02eddd)
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.
Example fix
// before: auth.json with only API-key entries
{ "default": { "key": "sk-..." } }
// after: re-login produces an OIDC-refreshable entry
{ "default": { "key": "sk-...", "refresh_token": "rt_...", "oidc_issuer": "https://issuer.example.com", "oidc_client_id": "client", "expires_at": "2026-09-01T00:00:00Z" } } Defensive patterns
Strategy: validation
Validate before calling
// Pre-check auth.json before building the provider
let content = std::fs::read_to_string(&auth_path)?;
let entries: BTreeMap<String, serde_json::Value> = serde_json::from_str(&content)?;
let has_oidc = entries.values().any(|v| {
v.get("refresh_token").is_some() && v.get("oidc_issuer").is_some()
});
if !has_oidc {
eprintln!("no OIDC entry in {} — run `grok login` first", auth_path.display());
} Type guard
fn is_oidc_entry(v: &serde_json::Value) -> bool {
v.get("refresh_token").map_or(false, |t| t.is_string())
&& v.get("oidc_issuer").map_or(false, |i| i.is_string())
} Try / catch
match read_auth_entry(&auth_path) {
Ok((scope_key, entry)) => build_provider(&scope_key, &entry)?,
Err(e) if e.to_string().contains("no OIDC auth entry") => {
prompt_grok_login()?; // recoverable: re-authenticate
}
Err(e) => return Err(e),
} Prevention
- 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.
When it happens
Trigger: 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 `{}`.
Common situations: 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.
Related errors
- auth entry has no oidc_issuer — cannot refresh expired token
- auth entry has no oidc_client_id — cannot refresh expired to
- auth entry has no refresh_token — cannot refresh expired tok
- upload parked: credentials rejected (HTTP 401); retrying in
- Failed to load config: {e}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/a24281c1d4393ade.
Report an issue: GitHub.