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
- 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.
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
- 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
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
- ResponseCode.ChannelAccessTokenFailed
- No response from Gemini
- ResponseCode.ChannelAuthSessionInvalid
- ResponseCode.ChannelAuthPlatformMismatch
- ResponseCode.ChannelAuthSessionCompleted
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/3e1c6ae8addbe9f9.
Report an issue: GitHub.