zed-industries/zed · warning

Sign-out occurred during token refresh

Error message

Sign-out occurred during token refresh

What it means

This error is raised inside the spawned token-refresh task when the credentials were refreshed successfully but the auth_generation counter on the State entity changed between refresh start and persist time. auth_generation is bumped on sign-out, so this indicates the user signed out while the refresh was in flight; persisting the refreshed credentials would resurrect a session the user explicitly terminated, so the refresh result is discarded with this error.

Solutions

  1. Treat as benign: retry the operation; a new get_fresh_credentials call will observe no credentials and surface NoApiKey, prompting normal re-authentication.
  2. Verify the sign-out path bumps auth_generation so this race guard fires correctly.
  3. Catch this error in completion streaming and stop the stream instead of showing a scary error to the user.
  4. Ensure UI sign-out cancels or detaches in-flight completion tasks to reduce occurrence.

Example fix

// before
let creds = get_fresh_credentials(&state, &http_client, cx).await?;
// after
let creds = match get_fresh_credentials(&state, &http_client, cx).await {
    Ok(creds) => creds,
    Err(e) if e.to_string().contains("Sign-out occurred during token refresh") => {
        return Err(LanguageModelCompletionError::NoApiKey { provider: PROVIDER_NAME });
    }
    Err(e) => return Err(e),
};
Defensive patterns

Strategy: try-catch

Validate before calling

let (auth_generation, has_creds) = state.read_with(&*cx, |s, _| (s.auth_generation, s.credentials.is_some()))?;
if !has_creds {
    return Err(anyhow!("signed out; skip completion"));
}

Type guard

fn is_signout_race(err: &LanguageModelCompletionError) -> bool {
    matches!(err, LanguageModelCompletionError::Other(e))
        && e.to_string().contains("Sign-out occurred during token refresh")
}

Try / catch

if let Err(e) = stream_open_ai_completion(request, cx).await {
    if is_signout_race(&e) {
        log::info!("user signed out mid-refresh; aborting stream silently");
        return Ok(()); // treat as user cancellation
    }
    return Err(e.into());
}

Prevention

When it happens

Trigger: A SuperGrok token refresh completes, but between capture of `generation` (state.auth_generation at refresh start) and the post-refresh check, something (sign_in/sign_out, fatal refresh clearing auth state) changed s.auth_generation. Concretely: user signs out while stream_open_ai_completion's background refresh is still awaiting the token endpoint.

Common situations: User clicks Sign out (or signs into a different account) while completion requests are in flight and a token refresh is running in the background; stale completion request tries to persist credentials for a signed-out session.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of zed-industries/zed@916fc2b8cb (2026-09-19). Data as JSON: /api/errors/1ecb86c2c473f4d1. Report an issue: GitHub.

Appendix: source

Thrown at crates/x_ai_subscribed/src/x_ai_subscribed.rs:624

    let previous_refresh_token = creds.refresh_token.clone();
    let previous_email = creds.email.clone();

    let generation = state
        .read_with(&*cx, |s, _| s.auth_generation)
        .map_err(LanguageModelCompletionError::Other)?;

    let shared_task = cx
        .spawn(async move |cx| {
            let result = refresh_token(&http_client_clone, &previous_refresh_token).await;

            match result {
                Ok(tokens) => {
                    let persist_result: Result<SuperGrokCredentials, Arc<anyhow::Error>> = async {
                        let current_generation = state_clone
                            .read_with(&*cx, |s, _| s.auth_generation)
                            .map_err(|e| Arc::new(e))?;
                        if current_generation != generation {
                            return Err(Arc::new(anyhow!(
                                "Sign-out occurred during token refresh"
                            )));
                        }

                        let claims = tokens
                            .id_token
                            .as_deref()
                            .map(extract_email_claim)
                            .unwrap_or(None);
                        let refreshed = SuperGrokCredentials {
                            access_token: tokens.access_token,
                            refresh_token: tokens
                                .refresh_token
                                .unwrap_or(previous_refresh_token.clone()),
                            expires_at_ms: now_ms() + tokens.expires_in * 1000,
                            email: claims.or(tokens.email).or(previous_email.clone()),
                        };

View on GitHub (pinned to 916fc2b8cb)