{"record":{"id":"cd5d4684aefb806b","repo":"yikart/AiToEarn","slug":"channelaccesstokenfailed","errorCode":"ChannelAccessTokenFailed","errorMessage":"ResponseCode.ChannelAccessTokenFailed","messagePattern":"ResponseCode\\.ChannelAccessTokenFailed","errorType":"exception","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts","lineNumber":793,"sourceCode":"    source: string,\n  ): Promise<void> {\n    await this.eventStream.emit(\n      EventStream.Channels,\n      EventTopic.ChannelsAccountConnected,\n      { userId, accountId, platform },\n      { source },\n    )\n  }\n\n  private shouldReassignDouyinAccount(source: string, platform: AccountType, credential: CredentialResult) {\n    return source === 'auth'\n      && platform === AccountType.Douyin\n      && credential.callbackResponseType === AuthCallbackResponseType.Json\n  }\n\n  private toCredentialContext(credential: CredentialResult): CredentialContext {\n    if (!credential.accessToken) {\n      throw new AppException(ResponseCode.ChannelAccessTokenFailed)\n    }\n\n    return {\n      accessToken: credential.accessToken,\n      refreshToken: credential.refreshToken,\n      expiresAt: credential.expiresAt,\n      scope: credential.scope,\n      platformUid: credential.platformUid,\n    }\n  }\n\n  getPlatformAuthViewFields(platform: AccountType): Pick<AuthViewFields, 'platformDisplayName' | 'platformLogoUrl'> {\n    const integration = this.registry.get(platform)\n    const locale = getLocale()\n    return {\n      platformDisplayName: integration.metadata.displayName[locale],\n      platformLogoUrl: integration.metadata.logoUrl,\n    }","sourceCodeStart":775,"sourceCodeEnd":811,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts#L775-L811","documentation":"toCredentialContext throws ChannelAccessTokenFailed (AppException, ResponseCode.ChannelAccessTokenFailed) when a CredentialResult obtained from a platform integration during the auth callback has an empty/missing accessToken. It is a defensive boundary between the platform integration result and the CredentialContext used to persist credentials — an integration returned success-shaped data without a token, which must never be stored.","triggerScenarios":"A channel integration's auth callback parsing produces a CredentialResult with no accessToken (platform returned an error payload the integration didn't map, mis-parsed response body, callbackResponseType mismatch e.g. expecting Json but receiving a redirect, or truncated query params in the OAuth redirect).","commonSituations":"Platform changed its OAuth callback payload shape after an API version update; wrong redirect URI config causing the code exchange to return an error body; Douyin/WeChat returning error codes the integration ignores; testing with a stub integration that returns partial credentials.","solutions":["Inspect the integration's token-exchange code and log the raw platform response to see why accessToken is missing.","Verify the OAuth app's redirect URI, client id/secret, and callbackResponseType config match the platform console settings.","Update the platform integration if the provider changed its token response format (API version drift).","Catch this at the callback layer and return a clear 'authorization failed, please retry' to the user rather than persisting partial credentials."],"exampleFix":"// before\nconst ctx = toCredentialContext(credential) // throws ChannelAccessTokenFailed\n// after\nif (!credential.accessToken) {\n  throw new AppException(ResponseCode.ChannelAccessTokenFailed, { platform, raw: rawResponse })\n}\nconst ctx = toCredentialContext(credential)","handlingStrategy":"validation","validationCode":"if (!credential || typeof credential.accessToken !== 'string' || credential.accessToken.length === 0) {\n  throw new AppException(ResponseCode.ChannelAccessTokenFailed, { platform })\n}","typeGuard":"function hasAccessToken(c: unknown): c is { accessToken: string } {\n  return !!c && typeof (c as any).accessToken === 'string' && (c as any).accessToken.length > 0\n}","tryCatchPattern":"try {\n  await completeAuth(sessionId, credential)\n} catch (e) {\n  if (e instanceof AppException && e.code === ResponseCode.ChannelAccessTokenFailed) {\n    return res.status(502).json({ message: 'Authorization failed at the platform, please retry' })\n  }\n  throw e\n}","preventionTips":["Log raw platform token-exchange responses (redacted) to detect payload changes early.","Validate OAuth callback query params (code, state) before exchanging.","Pin/verify platform OAuth API versions and review provider changelogs.","Add contract tests per integration asserting accessToken is present for a happy-path exchange."],"tags":["oauth","token","channels","integration"],"backgroundTag":"oauth-access-token-missing","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}