{"record":{"id":"f9e8ff5b893ada2e","repo":"yikart/AiToEarn","slug":"responsecode-channelauthplatformmismatch-f9e8ff","errorCode":"ResponseCode.ChannelAuthPlatformMismatch","errorMessage":"ChannelAuthPlatformMismatch","messagePattern":"ChannelAuthPlatformMismatch","errorType":"error_code","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts","lineNumber":112,"sourceCode":"      expiresAt,\n      authInstructions: integration.metadata.authInstructions,\n    }\n  }\n\n  async completeCallback(\n    platform: AccountType,\n    callbackInput: Omit<AuthCallbackInput, 'session'>,\n    sessionId: string,\n  ): Promise<AuthCallbackResult> {\n    const session = await this.redis.getChannelAuthSession<AuthSession>(sessionId)\n    if (!this.isAccountAuthSessionRecord(session)) {\n      throw new AppException(ResponseCode.ChannelAuthSessionInvalid)\n    }\n    if (this.isSessionExpired(session)) {\n      throw new AppException(ResponseCode.ChannelAuthSessionInvalid)\n    }\n    if (session.platform !== platform) {\n      throw new AppException(ResponseCode.ChannelAuthPlatformMismatch)\n    }\n    if (session.status !== ChannelAuthSessionStatus.Pending) {\n      throw new AppException(ResponseCode.ChannelAuthSessionCompleted)\n    }\n\n    const provider = this.registry.getAuth(platform)\n    const credentialResult = await provider.exchangeCode({\n      ...callbackInput,\n      session,\n    })\n\n    const credentialContext = credentialResult.accessToken\n      ? this.toCredentialContext(credentialResult)\n      : undefined\n    const profile = credentialResult.profile\n      ?? (credentialContext\n        ? await provider.getProfile(credentialContext)\n        : undefined)","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts#L94-L130","documentation":"Thrown in AuthService.completeCallback when the session stored in Redis was created for a different platform (session.platform !== platform argument). Each session is bound to one AccountType at creation; the callback URL path/platform must match it.","triggerScenarios":"completeCallback is invoked with platform X while the session (identified by state/sessionId) was started for platform Y — e.g. the provider redirects to the wrong callback route, the state value is copied between different platform connect attempts, or the client hardcodes the platform in the callback URL.","commonSituations":"Sharing one redirect_uri template across platforms with the platform segment templated wrong; copy-pasting a callback URL from another platform integration test; a provider sending users to a generic callback that routes to the wrong handler.","solutions":["Ensure the redirect/callback URL used when generating the auth URL matches the platform that will deliver the callback.","Never reuse a state/sessionId across platform connect flows; generate a fresh one per attempt.","Check the callback route registration so each platform's callback hits its own handler with the correct platform value.","If this is a test harness, start a new auth flow for the platform you are actually testing."],"exampleFix":"// before: session started for douyin, callback posted to tiktok route\nPOST /api/channels/auth/callback/tiktok?state=<douyinSessionId>\n\n// after: post to the platform the session was created for\nPOST /api/channels/auth/callback/douyin?state=<douyinSessionId>","handlingStrategy":"validation","validationCode":"const expectedPlatform = getPlatformFromCallbackRoute(req)\nconst session = await redis.getChannelAuthSession<AuthSession>(sessionId)\nif (session && session.platform !== expectedPlatform) {\n  throw new Error(`Callback platform ${expectedPlatform} != session platform ${session.platform}`)\n}","typeGuard":"function sessionMatchesPlatform(session: AuthSession | undefined, platform: AccountType): session is AuthSession & { platform: typeof platform } {\n  return !!session && session.platform === platform\n}","tryCatchPattern":"try {\n  await completeCallback(platform, input, sessionId)\n}\ncatch (e) {\n  if (getErrorCode(e) === ResponseCode.ChannelAuthPlatformMismatch) {\n    logError('callback routed to wrong platform handler', { platform, sessionId })\n    redirect_to_correct_platform_callback(platform)\n  }\n}","preventionTips":["Template redirect_uri per platform and assert it matches at flow start","Never share state across platform connect attempts","Add an integration test per platform callback route","Verify provider docs for exact callback path"],"tags":["oauth","session","platform-mismatch","routing"],"backgroundTag":"oauth-platform-mismatch","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}