yikart/AiToEarn · critical · AppException

ResponseCode.ChannelAuthPlatformUidMissing

ResponseCode.ChannelAuthPlatformUidMissing

Error message

ChannelAuthPlatformUidMissing

What it means

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.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts:132

    if (session.status !== ChannelAuthSessionStatus.Pending) {
      throw new AppException(ResponseCode.ChannelAuthSessionCompleted)
    }

    const provider = this.registry.getAuth(platform)
    const credentialResult = await provider.exchangeCode({
      ...callbackInput,
      session,
    })

    const credentialContext = credentialResult.accessToken
      ? this.toCredentialContext(credentialResult)
      : undefined
    const profile = credentialResult.profile
      ?? (credentialContext
        ? await provider.getProfile(credentialContext)
        : undefined)
    if (!profile) {
      throw new AppException(ResponseCode.ChannelAuthPlatformUidMissing)
    }
    const selectableAccounts = credentialResult.selectableAccounts
      ?? (!credentialResult.profile && credentialContext && provider.listSelectableAccounts
        ? await provider.listSelectableAccounts(credentialContext)
        : undefined)

    if (selectableAccounts) {
      const accounts = selectableAccounts
      if (accounts.length === 1) {
        const result = await this.connectSelectedAccountsForUser({
          userId: session.userId,
          platform: session.platform,
          selectableAccounts: accounts,
          selectedAccounts: [{
            platformUid: accounts[0].platformUid,
            account: accounts[0].account,
          }],
          groupId: session.groupId,

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Check the platform provider adapter: ensure exchangeCode returns a profile or a usable accessToken so getProfile can be called.
  2. Verify the requested OAuth scopes include the permissions needed to read user identity (platformUid).
  3. Reproduce the getProfile call manually with the obtained access token to see the raw provider response and fix the mapping.
  4. Update the adapter if the provider changed its user-info API shape or endpoint.

Example fix

// before: adapter returns neither profile nor token
return { callbackResponseType } // no profile, no accessToken

// after: always resolve identity during code exchange
const profile = await fetchUserInfo(accessToken)
return { accessToken, profile: { platformUid: profile.id, ... } }
Defensive patterns

Strategy: validation

Validate before calling

const credential = await provider.exchangeCode(input)
const profile = credential.profile ?? (credential.accessToken ? await provider.getProfile(toContext(credential)) : undefined)
if (!profile?.platformUid) {
  throw new Error('Provider returned no identity; check scopes and adapter profile mapping')
}

Type guard

function hasPlatformProfile(p: unknown): p is PlatformAccountProfile {
  return typeof p === 'object' && p !== null && typeof (p as any).platformUid === 'string' && (p as any).platformUid.length > 0
}

Try / catch

try {
  await completeCallback(platform, input, sessionId)
}
catch (e) {
  if (getErrorCode(e) === ResponseCode.ChannelAuthPlatformUidMissing) {
    logRawProviderResponse(input) // debug adapter mapping/scopes
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/3e1c6ae8addbe9f9. Report an issue: GitHub.