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

  1. Run `grok login` to regenerate auth.json with a full OIDC entry (refresh_token + oidc_issuer).
  2. Inspect the auth.json at the printed path and confirm at least one entry has non-null `refresh_token` and `oidc_issuer` fields.
  3. Verify `GROK_HOME`/`HOME` env vars point at the profile you actually logged in with.
  4. 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

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


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/a24281c1d4393ade. Report an issue: GitHub.