{"record":{"id":"292781699c77e280","repo":"xai-org/grok-build","slug":"oidcerror-tokenrefreshhttp","errorCode":null,"errorMessage":"OidcError::TokenRefreshHttp","messagePattern":"OidcError::TokenRefreshHttp","errorType":"http","errorClass":"OidcError","httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/auth/oidc/protocol.rs","lineNumber":555,"sourceCode":"        token_endpoint,\n    )\n    .send()\n    .await?;\n    if !resp.status().is_success() {\n        let status = resp.status().as_u16();\n        let body = resp.text().await.unwrap_or_default();\n        let error_code = serde_json::from_str::<serde_json::Value>(&body)\n            .ok()\n            .and_then(|v| v.get(\"error\")?.as_str().map(str::to_owned));\n        tracing::warn!(\n            http_status = status,\n            oauth2_error = ?error_code,\n            rt_prefix = xai_grok_auth::bearer_suffix(refresh_token),\n            client_id = %client_id,\n            principal_type = ?principal_type,\n            \"OIDC: token refresh HTTP error\"\n        );\n        return Err(anyhow::Error::new(OidcError::TokenRefreshHttp {\n            status,\n            body,\n        }));\n    }\n    Ok(resp.json().await?)\n}\n#[derive(Debug, Deserialize)]\npub(super) struct IdTokenClaims {\n    #[serde(default)]\n    pub(super) sub: Option<String>,\n    #[serde(default)]\n    pub(super) email: Option<String>,\n    #[serde(default)]\n    pub(super) iss: Option<String>,\n    #[serde(default)]\n    pub(super) aud: Option<serde_json::Value>,\n    #[serde(default)]\n    pub(super) nonce: Option<String>,","sourceCodeStart":537,"sourceCodeEnd":573,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/auth/oidc/protocol.rs#L537-L573","documentation":"OidcError::TokenRefreshHttp is raised when a refresh-token grant against the token endpoint returns a non-success HTTP status, carrying the status code and response body. The code also logs the oauth2 error code, the refresh-token suffix, client_id, and principal_type before returning this error. It means an existing session could not be renewed.","triggerScenarios":"The periodic/refresh path POSTs the refresh token to the token endpoint and gets a non-success status (commonly 400 invalid_grant), producing TokenRefreshHttp { status, body }.","commonSituations":"Refresh token revoked or expired (user logged out, password change, admin revocation), rotation consumed the old token, IdP policy changed, transient network/gateway errors.","solutions":["Re-run the login flow to obtain fresh tokens (invalid_grant cannot be refreshed)","Check the logged oauth2_error code: invalid_grant/expired_token means re-auth, invalid_client means fix credentials","If using token rotation, ensure only one client instance consumes each refresh token"],"exampleFix":"// before\n// silently failing refresh loop with stale token\nrefresh(access_token.refresh_token.clone()).await?;\n// after\nmatch refresh(rt).await {\n    Ok(t) => t,\n    Err(_) => return run_login_flow().await, // force re-login on invalid_grant\n}","handlingStrategy":"fallback","validationCode":"// before refreshing, confirm a refresh token exists and is not empty\nfn can_refresh(t: &SavedTokens) -> bool {\n    !t.refresh_token.is_empty()\n}","typeGuard":null,"tryCatchPattern":"match refresh(tokens.clone()).await {\n    Ok(fresh) => fresh,\n    Err(e) if matches!(e.downcast_ref::<OidcError>(), Some(OidcError::TokenRefreshHttp { status: 400, .. })) => {\n        // invalid_grant: refresh token dead — fall back to interactive login\n        run_login_flow().await?\n    }\n    Err(e) => return Err(e), // transient: surface or retry with backoff\n}","preventionTips":["Fall back to interactive login on 400 invalid_grant instead of looping","Do not share/consume one refresh token from multiple sessions if the IdP rotates tokens","Alert on refresh failures early rather than waiting for access-token expiry"],"tags":["oidc","oauth2","http","token-refresh"],"backgroundTag":"refresh-token-invalid","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}