{"record":{"id":"8b27c6cd39d21cf7","repo":"xai-org/grok-build","slug":"oidc-endpoint-rejected-request-status-body","errorCode":null,"errorMessage":"OIDC endpoint rejected request ({status}): {body}","messagePattern":"OIDC endpoint rejected request \\((.+?)\\): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-workspace/src/hub_auth/proactive.rs","lineNumber":692,"sourceCode":"        Some(exp) if Utc::now() < exp => after.min(remaining_std(exp, Utc::now())),\n        Some(_) => after.min(RETRY_CAP),\n        None => after,\n    };\n    // `remaining_std` is zero at/after expiry; never replace backoff with 0.\n    bounded.max(RETRY_BASE)\n}\n\n/// `400`/`401`/`403` are terminal. `429`/`408`/`425`/`5xx` stay on the retry\n/// backoff so a rate-limit cannot permanently stop `current()`.\nasync fn require_success(resp: reqwest::Response) -> Result<reqwest::Response, RefreshError> {\n    let status = resp.status();\n    if status.is_success() {\n        return Ok(resp);\n    }\n    let retry_after = parse_retry_after(&resp);\n    let body = resp.text().await.unwrap_or_default();\n    Err(RefreshError {\n        error: anyhow::anyhow!(\"OIDC endpoint rejected request ({status}): {body}\"),\n        new_refresh_token: None,\n        terminal: is_terminal_auth_status(status),\n        retry_after,\n    })\n}\n\nfn remaining_std(exp: DateTime<Utc>, now: DateTime<Utc>) -> Duration {\n    (exp - now).to_std().unwrap_or(Duration::ZERO)\n}\n\nfn datetime_plus(dt: DateTime<Utc>, d: Duration) -> Option<DateTime<Utc>> {\n    chrono::TimeDelta::from_std(d)\n        .ok()\n        .and_then(|td| dt.checked_add_signed(td))\n}\n\nfn datetime_minus(dt: DateTime<Utc>, d: Duration) -> Option<DateTime<Utc>> {\n    chrono::TimeDelta::from_std(d)","sourceCodeStart":674,"sourceCodeEnd":710,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-workspace/src/hub_auth/proactive.rs#L674-L710","documentation":"When a refresh-token exchange against the OIDC token endpoint returns a non-success HTTP status, the provider wraps the status and response body into a `RefreshError`. The error is terminal if the status indicates an unrecoverable auth problem (e.g. 400/401 invalid_grant) via `is_terminal_auth_status`, and a server-provided `Retry-After` header is propagated when present.","triggerScenarios":"POST to the issuer's token endpoint (refresh_token grant) returns 4xx/5xx: expired or revoked refresh token (invalid_grant), wrong client_id/client_secret, network-facing proxy errors (502/503), or rate limiting (429 with Retry-After).","commonSituations":"Refresh token rotated elsewhere (another machine) invalidating this copy; user revoked the session; issuer URL misconfigured pointing to the wrong host; corporate proxy returning HTML error pages; temporary IdP outage.","solutions":["If the status is 400/401 (invalid_grant), run `grok login` again — the refresh token chain is dead and cannot be recovered.","For 429/5xx, honor the `Retry-After` value and retry later; these are transient.","Verify oidc_issuer and oidc_client_id in auth.json match the values the refresh token was issued with.","Check for corporate proxies/firewalls altering token endpoint traffic."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match provider.refresh().await {\n    Ok(tokens) => use_tokens(tokens),\n    Err(e) if e.terminal => {\n        // invalid_grant / revoked token: only re-login recovers\n        eprintln!(\"refresh token rejected (terminal): {} — run `grok login`\", e.error);\n        prompt_grok_login()?;\n    }\n    Err(e) => {\n        // transient (429/5xx): respect server hint\n        let wait = e.retry_after.unwrap_or(Duration::from_secs(5));\n        tokio::time::sleep(wait).await;\n        retry_refresh()?;\n    }\n}","preventionTips":["Never run the same account on multiple machines without sharing auth.json — refresh-token rotation invalidates stale copies.","Honor Retry-After on 429 responses to avoid worsening rate limits.","Verify oidc_issuer/oidc_client_id after any IdP migration or client rename.","Monitor for terminal statuses and surface `grok login` to users instead of retrying forever."],"tags":["oidc","http","token-refresh","network","rust"],"backgroundTag":"oidc-token-refresh-failed","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}