zeroclaw-labs/zeroclaw · error
OpenAI Codex auth profile is missing token set: {profile_id}
Error message
OpenAI Codex auth profile is missing token set: {profile_id} What it means
The second-half twin of error 670 inside get_valid_openai_access_token: after acquiring the per-profile refresh lock, the function re-loads the store and requires the profile to still have a token_set. Between the first load and this re-load the profile lost its token_set — typically because a concurrent paste-token or profile edit rewrote it to token kind while a refresh was in flight.
Source
Thrown at crates/zeroclaw-providers/src/auth/mod.rs:236
if !token_set.is_expiring_within(Duration::from_secs(OPENAI_REFRESH_SKEW_SECS)) {
return Ok(Some(token_set.access_token.clone()));
}
let Some(refresh_token) = token_set.refresh_token.clone() else {
return Ok(Some(token_set.access_token.clone()));
};
let refresh_lock = refresh_lock_for_profile(&profile_id);
let _guard = refresh_lock.lock().await;
// Re-load after waiting for lock to avoid duplicate refreshes.
let data = self.store.load().await?;
let Some(latest_profile) = data.profiles.get(&profile_id) else {
return Ok(None);
};
let Some(latest_tokens) = latest_profile.token_set.as_ref() else {
anyhow::bail!("OpenAI Codex auth profile is missing token set: {profile_id}");
};
if !latest_tokens.is_expiring_within(Duration::from_secs(OPENAI_REFRESH_SKEW_SECS)) {
return Ok(Some(latest_tokens.access_token.clone()));
}
let refresh_token = latest_tokens.refresh_token.clone().unwrap_or(refresh_token);
if let Some(remaining) = refresh_backoff_remaining(&profile_id) {
anyhow::bail!(
"OpenAI token refresh is in backoff for {remaining}s due to previous failures"
);
}
let mut refreshed =
match refresh_openai_access_token_with_retries(&self.client, &refresh_token).await {
Ok(tokens) => {
clear_refresh_backoff(&profile_id);View on GitHub (pinned to 88bb9c8533)
Solutions
- Re-run the credential resolution after the concurrent write finishes — the profile state seen on the next load is consistent
- Avoid mixing profile kinds: keep an openai-codex profile either OAuth (auth login) or bearer (paste-token), not both written by racing processes
- Give each zeroclaw instance its own state dir so profile files are not shared
Defensive patterns
Strategy: retry
Validate before calling
// Serialize profile writes: a single writer prevents token_set from vanishing mid-refresh.
// Before resolving credentials, confirm the profile still holds a token set.
let data = auth.load_profiles().await?;
anyhow::ensure!(
data.profiles.get(&profile_id).map(|p| p.token_set.is_some()).unwrap_or(false),
"profile lost its token set; wait for concurrent auth writes to finish"
); Type guard
fn is_oauth_profile(p: &AuthProfile) -> bool {
p.token_set.is_some()
} Try / catch
match auth.get_valid_openai_access_token(override_).await {
Ok(tok) => tok,
Err(e) if e.to_string().contains("missing token set") => {
// profile was rewritten between loads; reload and retry once
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
auth.get_valid_openai_access_token(override_).await?
}
Err(e) => return Err(e),
} Prevention
- Give each process its own state dir so profile files have one writer
- Do not run paste-token while a gateway is refreshing the same profile
- Retry once after a short delay when the error appears during concurrent auth activity
When it happens
Trigger: Two tasks call get_valid_openai_access_token around the same time the same profile is overwritten by store_model_provider_token (bearer token), or the profiles file is edited externally between the two loads; the first load saw a token_set, the post-lock load does not.
Common situations: A CLI auth paste-token runs while a long-lived gateway process refreshes tokens; scripts rewriting the auth profiles JSON concurrently; profiles file shared between two zeroclaw instances.
Related errors
- OpenAI Codex auth profile is not OAuth-based: {profile_id}
- Gemini auth profile is missing token set: {profile_id}
- xAI auth profile is missing token set: {profile_id}
- OpenAI token refresh is in backoff for {remaining}s due to p
- Gemini auth profile is not OAuth-based: {profile_id}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/736030047771ae8c.
Report an issue: GitHub.