decolua/9router · error
Invalid state parameter
Error message
Invalid state parameter
What it means
connect() generates a random `state` value, embeds it in the authorize URL, and requires the callback to echo the identical state. A mismatch means the callback did not originate from the authorization request this run started — the library refuses it as a CSRF protection. The flow is aborted before the token exchange.
Source
Thrown at src/lib/oauth/services/xai.js:220
spinner.start("Waiting for xAI authorization...");
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error("Authentication timeout (5 minutes)")), 300000);
const iv = setInterval(() => {
if (callbackParams) {
clearInterval(iv);
clearTimeout(timeout);
resolve();
}
}, 100);
});
close();
if (callbackParams.error) {
throw new Error(callbackParams.error_description || callbackParams.error);
}
if (!callbackParams.code) throw new Error("No authorization code received");
if (callbackParams.state !== state) throw new Error("Invalid state parameter");
spinner.start("Exchanging code for tokens...");
const tokens = await this.exchangeXaiCode({
tokenUrl,
code: callbackParams.code,
redirectUri,
codeVerifier,
});
const email = decodeIdTokenEmail(tokens.id_token);
spinner.succeed("xAI connected successfully!");
return { tokens, email };
} catch (error) {
spinner.fail(`Failed: ${error.message}`);
throw error;
}
}
}View on GitHub (pinned to 90b52e06ff)
Solutions
- Re-run connect() and use the current browser tab/URL it prints — do not reuse an auth URL from a previous attempt
- Close stale auth tabs from earlier attempts so an old callback cannot arrive first
- Avoid running two connect() flows concurrently for the same provider
- Do not modify the authUrl; let open() launch it exactly as generated
- If a corporate proxy rewrites URLs, bypass it for 127.0.0.1 loopback redirects
Defensive patterns
Strategy: try-catch
Validate before calling
// Verify state on your own callback handler before relying on connect().
const params = new URL(callbackUrl).searchParams;
if (params.get('state') !== expectedState) {
console.log('State mismatch — stale or forged callback; restart the flow.');
} Try / catch
try {
await xai.connect();
} catch (err) {
if (err.message === 'Invalid state parameter') {
// stale/concurrent flow — rerun connect() with a fresh tab
} else {
throw err;
}
} Prevention
- Use the auth URL printed by the current run, never an old one
- Close stale authorization tabs from previous attempts
- Run only one OAuth flow at a time per provider
- Don't hand-edit the authUrl or its query string
- Bypass proxies that rewrite URLs for 127.0.0.1 redirects
When it happens
Trigger: During xaiOAuthService.connect(), the callback's `state` query parameter differs from the `state` generated for this session (callbackParams.state !== state).
Common situations: Two connect() runs overlap and a stale browser tab from an earlier attempt delivers the old callback; the state got truncated/URL-encoded differently in transit (manual URL editing, redirect through a tool that rewrites the query); sticky browser cache replayed an old redirect; the user copy-pasted a previous authUrl instead of the freshly printed one.
Related errors
- Windsurf callback state mismatch
- This request isn't valid. Please restart the Kimchi login fl
- ${callbackParams.error_description || callbackParams.error}
- No authorization code received
- xAI OAuth session not found; restart the login flow and past
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/d386e384313eeb41.
Report an issue: GitHub.