{"record":{"id":"61549b8179029e2b","repo":"nexu-io/open-design","slug":"xai-oauth-state-mismatch-expected-serverid-xai","errorCode":null,"errorMessage":"xAI OAuth state mismatch: expected serverId=${XAI_PROVIDER_ID}, got ${consumed.serverId}","messagePattern":"xAI OAuth state mismatch: expected serverId=(.+?), got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/daemon/src/integrations/xai-oauth.ts","lineNumber":134,"sourceCode":"  code: string;\n  fetchImpl?: typeof fetch;\n}\n\n/**\n * Post-callback half of the OAuth dance. Looks up `state` in `pending`,\n * validates it (one-shot, TTL-checked by `PendingAuthCache`), and\n * exchanges `code` for tokens. Throws if `state` is unknown, expired,\n * already consumed, or was issued for a different provider.\n */\nexport async function completeXAIAuth(\n  input: CompleteXAIAuthInput,\n): Promise<OAuthTokenResponse> {\n  const consumed = input.pending.consume(input.state);\n  if (!consumed) {\n    throw new Error('xAI OAuth state not found or expired');\n  }\n  if (consumed.serverId !== XAI_PROVIDER_ID) {\n    throw new Error(\n      `xAI OAuth state mismatch: expected serverId=${XAI_PROVIDER_ID}, got ${consumed.serverId}`,\n    );\n  }\n  return exchangeCodeForToken(\n    {\n      tokenEndpoint: consumed.tokenEndpoint,\n      clientId: consumed.clientId,\n      redirectUri: consumed.redirectUri,\n      code: input.code,\n      codeVerifier: consumed.codeVerifier,\n    },\n    input.fetchImpl ?? fetch,\n  );\n}\n\nexport interface RefreshXAITokenInput {\n  refreshToken: string;\n  fetchImpl?: typeof fetch;","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/integrations/xai-oauth.ts#L116-L152","documentation":"Raised by completeXAIAuth() after a successful consume() when the stored PendingAuthState.serverId does not equal XAI_PROVIDER_ID ('xai'). The PendingAuthCache is shared across providers, so a consumed state must be validated to belong to the xAI flow before exchanging its code — otherwise one provider's code would be exchanged against another provider's token endpoint.","triggerScenarios":"A callback for a different OAuth provider (sharing the same PendingAuthCache and state space) lands on the xAI completion path, or a state value collided across providers. The consume succeeded (state was valid) but serverId mismatch proves cross-provider routing.","commonSituations":"Two providers use the same loopback redirect port and a callback for provider A reaches the xAI handler; state generation produced a duplicate across providers (very unlikely with generateState); test fixtures seeding states with the wrong serverId; refactor that changed a provider id but not stored states.","solutions":["Route the callback by provider: dispatch to the matching complete*Auth based on which authorize flow issued the state, not a fixed xAI path.","Use provider-scoped redirect ports/paths so cross-provider callbacks cannot collide.","If seeding PendingAuthState in tests, set serverId: XAI_PROVIDER_ID."],"exampleFix":"// before\nif (consumed.serverId !== XAI_PROVIDER_ID) {\n  throw new Error(`xAI OAuth state mismatch: expected serverId=${XAI_PROVIDER_ID}, got ${consumed.serverId}`);\n}\n\n// after (route to the correct provider instead of throwing)\nif (consumed.serverId !== XAI_PROVIDER_ID) {\n  return routeToProviderCallback(consumed.serverId, input);\n}","handlingStrategy":"validation","validationCode":"import { XAI_PROVIDER_ID } from '../integrations/xai-oauth.js';\n\nfunction stateBelongsToXai(consumed: { serverId: string }): boolean {\n  return consumed.serverId === XAI_PROVIDER_ID;\n}\n\nconst consumed = input.pending.peek(input.state); // if your cache exposes a non-consuming peek\nif (consumed && !stateBelongsToXai(consumed)) {\n  return { ok: false, reason: 'wrong_provider' };\n}","typeGuard":"function isOAuthStateMismatch(err: unknown): boolean {\n  return err instanceof Error && err.message.startsWith('xAI OAuth state mismatch:');\n}","tryCatchPattern":"try {\n  return await completeXAIAuth(input);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('xAI OAuth state mismatch:')) {\n    // route to the provider that actually owns this state\n    return routeToProviderCallback(extractServerId(err), input);\n  }\n  throw err;\n}","preventionTips":["Use provider-scoped redirect ports/paths so cross-provider callbacks cannot collide.","Dispatch callbacks to the matching provider handler, not a fixed xAI path.","When seeding PendingAuthState, always set serverId to the issuing provider id."],"tags":["oauth","integration","auth","validation","xai"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}