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

  1. Re-run the credential resolution after the concurrent write finishes — the profile state seen on the next load is consistent
  2. Avoid mixing profile kinds: keep an openai-codex profile either OAuth (auth login) or bearer (paste-token), not both written by racing processes
  3. 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

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


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/736030047771ae8c. Report an issue: GitHub.