{"record":{"id":"48559e7c61d75ce8","repo":"zeroclaw-labs/zeroclaw","slug":"openai-token-refresh-is-in-backoff-for-remaining","errorCode":null,"errorMessage":"OpenAI token refresh is in backoff for {remaining}s due to previous failures","messagePattern":"OpenAI token refresh is in backoff for (.+?)s due to previous failures","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/zeroclaw-providers/src/auth/mod.rs","lineNumber":246,"sourceCode":"\n        // Re-load after waiting for lock to avoid duplicate refreshes.\n        let data = self.store.load().await?;\n        let Some(latest_profile) = data.profiles.get(&profile_id) else {\n            return Ok(None);\n        };\n\n        let Some(latest_tokens) = latest_profile.token_set.as_ref() else {\n            anyhow::bail!(\"OpenAI Codex auth profile is missing token set: {profile_id}\");\n        };\n\n        if !latest_tokens.is_expiring_within(Duration::from_secs(OPENAI_REFRESH_SKEW_SECS)) {\n            return Ok(Some(latest_tokens.access_token.clone()));\n        }\n\n        let refresh_token = latest_tokens.refresh_token.clone().unwrap_or(refresh_token);\n\n        if let Some(remaining) = refresh_backoff_remaining(&profile_id) {\n            anyhow::bail!(\n                \"OpenAI token refresh is in backoff for {remaining}s due to previous failures\"\n            );\n        }\n\n        let mut refreshed =\n            match refresh_openai_access_token_with_retries(&self.client, &refresh_token).await {\n                Ok(tokens) => {\n                    clear_refresh_backoff(&profile_id);\n                    tokens\n                }\n                Err(err) => {\n                    set_refresh_backoff(\n                        &profile_id,\n                        Duration::from_secs(OPENAI_REFRESH_FAILURE_BACKOFF_SECS),\n                    );\n                    return Err(err);\n                }\n            };","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-providers/src/auth/mod.rs#L228-L264","documentation":"get_valid_openai_access_token attempted a refresh path and hit the in-process backoff guard: a previous refresh attempt already failed within the last OPENAI_REFRESH_FAILURE_BACKOFF_SECS (10 s), so refresh_backoff_remaining returned a remaining window and the call bails fast instead of hammering the token endpoint. The backoff lives in a process-wide static map keyed by profile_id and clears on success or expiry.","triggerScenarios":"The cached access token is within 90 s of expiry (OPENAI_REFRESH_SKEW_SECS) or past it, a refresh_token exists, and an earlier refresh_openai_access_token_with_retries call failed — every get_valid_openai_access_token call within the following 10 seconds bails with the remaining seconds embedded.","commonSituations":"A short outage at the OpenAI token endpoint while a scheduler retries authentication repeatedly; the refresh token was revoked server-side so each refresh fails and callers stack up on the backoff; multiple tasks resolving credentials at boot after the network dropped.","solutions":["Wait the embedded number of seconds (plus margin) and retry the call — the backoff expires automatically","Find the underlying refresh failure: run auth refresh --model-provider openai-codex and read the real error (often an expired/revoked refresh token)","If the refresh token is revoked, re-authenticate with auth login --model-provider openai-codex","Restarting the process also clears the in-memory backoff, but only do that after fixing the root cause"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// The backoff window is at most 10s and lives in-process; schedule resolution\n// at least that long after any known refresh failure.\n// (No public accessor exists — pace your own retries instead.)\ntokio::time::sleep(std::time::Duration::from_secs(10)).await;\nlet token = auth.get_valid_openai_access_token(None).await?;","typeGuard":null,"tryCatchPattern":"match auth.get_valid_openai_access_token(None).await {\n    Ok(tok) => tok,\n    Err(e) => {\n        let msg = e.to_string();\n        if let Some(rest) = msg.split(\"backoff for \").nth(1) {\n            let secs: u64 = rest.split('s').next().unwrap_or(\"0\").parse().unwrap_or(1);\n            tokio::time::sleep(std::time::Duration::from_secs(secs + 1)).await;\n            return auth.get_valid_openai_access_token(None).await;\n        }\n        Err(e)\n    }\n}","preventionTips":["Space credential-resolution attempts more than 10 s apart after a failure","Diagnose the underlying refresh error instead of retrying through the guard forever","Remember the backoff map is per-process: a restart clears it, fixing nothing"],"tags":["auth","oauth","openai-codex","backoff","rate-limit","rust"],"backgroundTag":"token-refresh-backoff","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}