musistudio/claude-code-router · error · Error

Grok CLI OIDC discovery did not return a token endpoint.

Error message

Grok CLI OIDC discovery did not return a token endpoint.

What it means

OIDC discovery for Grok succeeded at the HTTP level but the returned JSON lacked a token_endpoint (or tokenEndpoint) string, so no OAuth token URL could be derived. The library throws instead of guessing an endpoint.

Solutions

  1. Fetch the discovery URL manually and inspect the JSON for token_endpoint
  2. Re-run login to regenerate the cached discovery URL
  3. Verify the issuer matches the actual OIDC provider host
  4. Report if the provider changed the discovery schema
Defensive patterns

Strategy: validation

Validate before calling

const doc = await fetch(discoveryUrl).then(r => r.json());
if (typeof doc.token_endpoint !== 'string' || !doc.token_endpoint) {
  throw new Error('discovery document lacks token_endpoint — refresh login config');
}

Try / catch

catch (e) {
  if (e instanceof Error && e.message.includes('did not return a token endpoint')) {
    await relogin(); // cached discovery URL is wrong
  }
}

Prevention

When it happens

Trigger: The discovery document JSON parses but has no token_endpoint/tokenEndpoint field — a truncated body, a non-standard discovery document, or the URL pointing at a JSON resource that is not an OIDC configuration.

Common situations: Discovery URL points at the wrong well-known path; provider ships a partial/preview document; a proxy rewrites the response body.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/8eaba03cf152a14a. Report an issue: GitHub.

Appendix: source

Thrown at packages/core/src/agents/local-providers/grok.ts:753

  }
  const issuer = (auth.oidcIssuer || readString(process.env.GROK_OIDC_ISSUER) || grokDefaultOidcIssuer).replace(/\/+$/, "");
  const metadataUrl = `${issuer}/.well-known/openid-configuration`;
  const timeoutMs = normalizeGrokOauthTimeout(process.env.GROK_OIDC_REFRESH_TIMEOUT_MS);
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  try {
    const response = await fetchWithSystemProxy(metadataUrl, {
      headers: { accept: "application/json" },
      signal: controller.signal
    });
    const text = await response.text();
    const payload = parseJsonRecord(text);
    if (!response.ok) {
      throw new Error(`Grok CLI OIDC discovery returned HTTP ${response.status}${tokenRefreshErrorMessage(payload, text)}`);
    }
    const tokenEndpoint = readString(payload?.token_endpoint) || readString(payload?.tokenEndpoint);
    if (!tokenEndpoint) {
      throw new Error("Grok CLI OIDC discovery did not return a token endpoint.");
    }
    return tokenEndpoint;
  } catch (error) {
    if (error instanceof Error && error.name === "AbortError") {
      throw new Error(`Grok CLI OIDC discovery timed out after ${timeoutMs}ms.`);
    }
    throw error;
  } finally {
    clearTimeout(timer);
  }
}

function grokCredentialFiles(): string[] {
  const explicitFile = process.env.GROK_AUTH_FILE?.trim();
  return uniqueStrings([
    explicitFile,
    path.join(grokStorageRoot(), "auth.json"),
    path.join(grokStorageRoot(), "credentials.json")

View on GitHub (pinned to 99f24806c6)