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
- Restart the OAuth flow from startXAIAuth to mint a fresh state, then complete promptly within the TTL.
- Ensure the callback hits the same daemon process that started the flow (sticky routing / single worker).
- 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
- Route callbacks to the same daemon process that started the flow (sticky routing).
- Complete the callback promptly within the PendingAuthCache TTL.
- Dedupe callbacks on state to avoid double-consume (one-shot cache).
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
- xAI OAuth state mismatch: expected serverId=${XAI_PROVIDER_I
- no xAI credentials — sign in with your SuperGrok subscriptio
- WORKSPACE_AUTHORITY_UNAVAILABLE
- codex mcp add failed: ${failureDetail(result)}
- codex mcp remove failed: ${failureDetail(result)}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/7ba1286823fd8be7.
Report an issue: GitHub.