{"record":{"id":"3e1c6ae8addbe9f9","repo":"yikart/AiToEarn","slug":"responsecode-channelauthplatformuidmissing","errorCode":"ResponseCode.ChannelAuthPlatformUidMissing","errorMessage":"ChannelAuthPlatformUidMissing","messagePattern":"ChannelAuthPlatformUidMissing","errorType":"error_code","errorClass":"AppException","httpStatus":null,"severity":"critical","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts","lineNumber":132,"sourceCode":"    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)\n    if (!profile) {\n      throw new AppException(ResponseCode.ChannelAuthPlatformUidMissing)\n    }\n    const selectableAccounts = credentialResult.selectableAccounts\n      ?? (!credentialResult.profile && credentialContext && provider.listSelectableAccounts\n        ? await provider.listSelectableAccounts(credentialContext)\n        : undefined)\n\n    if (selectableAccounts) {\n      const accounts = selectableAccounts\n      if (accounts.length === 1) {\n        const result = await this.connectSelectedAccountsForUser({\n          userId: session.userId,\n          platform: session.platform,\n          selectableAccounts: accounts,\n          selectedAccounts: [{\n            platformUid: accounts[0].platformUid,\n            account: accounts[0].account,\n          }],\n          groupId: session.groupId,","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts#L114-L150","documentation":"Thrown in AuthService.completeCallback when, after exchanging the authorization code, no platform profile (containing platformUid) can be obtained — the provider's exchangeCode returned no profile and provider.getProfile(credentialContext) was unavailable or returned undefined. The system cannot identify which platform account was authorized.","triggerScenarios":"completeCallback reaches profile resolution when: the provider adapter's exchangeCode omits profile and no accessToken is present (so no credentialContext to call getProfile with); getProfile is not implemented or fails to return a profile for the credential.","commonSituations":"A provider API change where the userinfo endpoint returns a different shape and the adapter fails to map platformUid; missing API scope so the user-info call is rejected; an adapter where exchangeCode returns only a code but not an access token (deferred exchange) and getProfile isn't wired.","solutions":["Check the platform provider adapter: ensure exchangeCode returns a profile or a usable accessToken so getProfile can be called.","Verify the requested OAuth scopes include the permissions needed to read user identity (platformUid).","Reproduce the getProfile call manually with the obtained access token to see the raw provider response and fix the mapping.","Update the adapter if the provider changed its user-info API shape or endpoint."],"exampleFix":"// before: adapter returns neither profile nor token\nreturn { callbackResponseType } // no profile, no accessToken\n\n// after: always resolve identity during code exchange\nconst profile = await fetchUserInfo(accessToken)\nreturn { accessToken, profile: { platformUid: profile.id, ... } }","handlingStrategy":"validation","validationCode":"const credential = await provider.exchangeCode(input)\nconst profile = credential.profile ?? (credential.accessToken ? await provider.getProfile(toContext(credential)) : undefined)\nif (!profile?.platformUid) {\n  throw new Error('Provider returned no identity; check scopes and adapter profile mapping')\n}","typeGuard":"function hasPlatformProfile(p: unknown): p is PlatformAccountProfile {\n  return typeof p === 'object' && p !== null && typeof (p as any).platformUid === 'string' && (p as any).platformUid.length > 0\n}","tryCatchPattern":"try {\n  await completeCallback(platform, input, sessionId)\n}\ncatch (e) {\n  if (getErrorCode(e) === ResponseCode.ChannelAuthPlatformUidMissing) {\n    logRawProviderResponse(input) // debug adapter mapping/scopes\n  }\n}","preventionTips":["Request identity scopes in every provider OAuth config","Unit-test each adapter's exchangeCode/getProfile against recorded provider payloads","Assert platformUid presence in adapter responses with schema validation","Watch provider changelogs for user-info API changes"],"tags":["oauth","platform","profile","adapter"],"backgroundTag":"oauth-identity-missing","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}