nexu-io/open-design · error

xAI OAuth state not found or expired

Error message

xAI OAuth state not found or expired

What it means

Raised by completeXAIAuth() when input.pending.consume(input.state) returns null. PendingAuthCache.consume is a one-shot, TTL-checked lookup: it returns null if the state is unknown, expired, already consumed, or the cache was never seeded by the matching startXAIAuth call. This is the post-callback half of the PKCE OAuth flow.

Source

Thrown at apps/daemon/src/integrations/xai-oauth.ts:131

export interface CompleteXAIAuthInput {
  pending: PendingAuthCache;
  state: string;
  code: string;
  fetchImpl?: typeof fetch;
}

/**
 * Post-callback half of the OAuth dance. Looks up `state` in `pending`,
 * validates it (one-shot, TTL-checked by `PendingAuthCache`), and
 * exchanges `code` for tokens. Throws if `state` is unknown, expired,
 * already consumed, or was issued for a different provider.
 */
export async function completeXAIAuth(
  input: CompleteXAIAuthInput,
): Promise<OAuthTokenResponse> {
  const consumed = input.pending.consume(input.state);
  if (!consumed) {
    throw new Error('xAI OAuth state not found or expired');
  }
  if (consumed.serverId !== XAI_PROVIDER_ID) {
    throw new Error(
      `xAI OAuth state mismatch: expected serverId=${XAI_PROVIDER_ID}, got ${consumed.serverId}`,
    );
  }
  return exchangeCodeForToken(
    {
      tokenEndpoint: consumed.tokenEndpoint,
      clientId: consumed.clientId,
      redirectUri: consumed.redirectUri,
      code: input.code,
      codeVerifier: consumed.codeVerifier,
    },
    input.fetchImpl ?? fetch,
  );
}

View on GitHub (pinned to 5be4028344)

Solutions

  1. Restart the OAuth flow from startXAIAuth to mint a fresh state, then complete promptly within the TTL.
  2. Ensure the callback hits the same daemon process that started the flow (sticky routing / single worker).
  3. Avoid double-processing the callback URL (dedupe on state).

Example fix

// before
const consumed = input.pending.consume(input.state);
if (!consumed) throw new Error('xAI OAuth state not found or expired');

// after (return a structured result so the route can render a friendly page)
const consumed = input.pending.consume(input.state);
if (!consumed) {
  return { ok: false, reason: 'state_expired_or_unknown' };
}
Defensive patterns

Strategy: try-catch

Validate before calling

function isPlausibleOAuthState(state: unknown): state is string {
  return typeof state === 'string' && state.length > 16;
}

// usage: reject obviously bad callbacks before consuming
if (!isPlausibleOAuthState(input.state)) {
  return { ok: false, reason: 'invalid_state' };
}

Type guard

function isOAuthStateNotFound(err: unknown): boolean {
  return err instanceof Error && err.message === 'xAI OAuth state not found or expired';
}

Try / catch

try {
  return await completeXAIAuth(input);
} catch (err) {
  if (err instanceof Error && err.message === 'xAI OAuth state not found or expired') {
    return { ok: false, reason: 'state_expired_or_unknown' };
  }
  throw err;
}

Prevention

When it happens

Trigger: The OAuth callback arrives with a state value that was never stored (startXAIAuth not called), was stored but TTL-expired, was already consumed (double callback / refresh), or belongs to a different daemon process whose in-memory cache did not hold it.

Common situations: User opened the callback URL twice (browser pre-fetch + real navigation); too much time between authorize and callback (TTL); daemon restarted between starting auth and the callback (in-memory cache lost); callback received by a different worker/process; state parameter corrupted in transit.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/7ba1286823fd8be7. Report an issue: GitHub.