{"record":{"id":"afc976f268f332fd","repo":"decolua/9router","slug":"invalid-state-parameter","errorCode":null,"errorMessage":"Invalid state parameter","messagePattern":"Invalid state parameter","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/lib/oauth/services/oauth.js","lineNumber":146,"sourceCode":"\n    // Start local server and get redirect URI\n    const { redirectUri, waitForCallback } = await this.startAuthFlow(null, providerName);\n\n    // Build authorization URL\n    const authUrl = buildAuthUrlFn(redirectUri, state, codeChallenge);\n\n    console.log(`\\nOpening browser for ${providerName} authentication...`);\n    console.log(`If browser doesn't open, visit:\\n${authUrl}\\n`);\n\n    // Open browser\n    await open(authUrl);\n\n    // Wait for callback\n    const callbackParams = await waitForCallback();\n\n    // Validate state\n    if (callbackParams.state !== state) {\n      throw new Error(\"Invalid state parameter\");\n    }\n\n    return {\n      code: callbackParams.code,\n      state: callbackParams.state,\n      codeVerifier,\n      redirectUri,\n    };\n  }\n}\n\n","sourceCodeStart":128,"sourceCodeEnd":158,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/services/oauth.js#L128-L158","documentation":"Thrown by OAuthService.authenticate() after the callback arrives: the `state` query param returned by the provider does not equal the random `state` generated locally by generatePKCE() at the start of the flow. This is a CSRF/session-mixup guard — the library refuses to proceed because the callback may not belong to this authentication attempt.","triggerScenarios":"authenticate() -> waitForCallback() resolves with callbackParams whose `state` differs from the locally generated one — e.g. a stale tab from a previous run completing the callback on the reused localhost port, two CLI auth flows running concurrently on the same port, the provider dropping/rewriting the state param, or the callback URL being manually edited.","commonSituations":"An old browser tab from a previous failed auth attempt finally redirects and hits the new local server first; running two `connect` flows in parallel terminals where one server steals the other's callback; proxies or SSO intermediaries stripping query parameters; cookie/partitioned browser sessions mixing flows.","solutions":["Close all stale tabs from previous auth attempts and restart the flow in a single browser tab.","Run only one authentication flow at a time; concurrent flows can bind the same localhost port and cross-deliver callbacks.","Re-run the flow and complete it promptly (within the 5-minute timeout) without manually editing the URL.","If a proxy strips query params, bypass it for localhost callbacks or complete auth outside the proxied network."],"exampleFix":"// before\nconst callbackParams = await waitForCallback();\nif (callbackParams.state !== state) {\n  throw new Error(\"Invalid state parameter\");\n}\n// after (fail fast with both values for diagnosis)\nif (callbackParams.state !== state) {\n  throw new Error(`Invalid state parameter: expected ${state}, got ${callbackParams.state}`);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const result = await service.authenticate(provider, buildUrlFn);\n} catch (err) {\n  if (err.message === \"Invalid state parameter\") {\n    // stale tab or concurrent flow — clean up and retry once, serially\n  } else throw err;\n}","preventionTips":["Run only one OAuth flow at a time; concurrent flows can share the localhost callback port.","Close leftover tabs from previous failed auth attempts before retrying.","Complete the flow within the timeout window without manually editing URLs.","Don't bookmark or reuse authorize URLs — each flow needs a fresh state."],"tags":["oauth","csrf","state-parameter","pkce"],"backgroundTag":"oauth-state-mismatch","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}