{"record":{"id":"e1f92466ea5e6cfc","repo":"yikart/AiToEarn","slug":"channelauthplatformuidmissing-e1f924","errorCode":"ChannelAuthPlatformUidMissing","errorMessage":"ChannelAuthPlatformUidMissing","messagePattern":"ChannelAuthPlatformUidMissing","errorType":"error_code","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/wechat/wechat-official/wechat-official-auth.provider.ts","lineNumber":90,"sourceCode":"\n    return {\n      accessToken: result.accessToken,\n      refreshToken: result.refreshToken,\n      expiresAt: new Date(Date.now() + result.expiresIn * 1000),\n      scope: result.scope,\n    }\n  }\n\n  async revoke(_input: RevokeCredentialInput): Promise<void> {\n    // WeChat Official Account OAuth does not support server-side token revocation.\n    // Tokens expire naturally (2 hours for access, 30 days for refresh).\n    this.logger.log('WeChat Official Account tokens expire naturally; no revocation needed')\n  }\n\n  async getProfile(input: CredentialContext): Promise<PlatformAccountProfile> {\n    const openId = input.platformUid\n    if (!openId) {\n      throw new AppException(ResponseCode.ChannelAuthPlatformUidMissing)\n    }\n\n    const userInfo = await this.wechatService.getOfficialUserInfo(\n      input.accessToken,\n      openId,\n    )\n\n    return {\n      platformUid: userInfo.openId,\n      displayName: userInfo.nickname,\n      avatarUrl: userInfo.avatarUrl,\n      raw: {\n        unionId: userInfo.unionId,\n      },\n    }\n  }\n}\n","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/wechat/wechat-official/wechat-official-auth.provider.ts#L72-L108","documentation":"getProfile needs the platform's openId (platformUid) to call WeChat's user info endpoint. When input.platformUid is missing, AppException(ResponseCode.ChannelAuthPlatformUidMissing) is thrown, since the Official Account user-info API cannot be queried without the openid.","triggerScenarios":"Calling getProfile with a CredentialContext built from OAuth flows that did not capture openid, or from credential records where platformUid was never saved.","commonSituations":"Channels connected via a flow that only stored the access token, older credentials predating platformUid persistence, copying credentials between accounts losing the uid mapping.","solutions":["Re-run the OAuth flow so openid is captured and stored as platformUid","Backfill platformUid from WeChat's /cgi-bin/user/info or OAuth userinfo response for existing credentials","Guard call sites: skip getProfile when platformUid is absent and schedule re-auth instead","Verify the credential context builder always maps WeChat openid into platformUid"],"exampleFix":"// before\nconst profile = await authProvider.getProfile({ accessToken })\n// after\nif (!credential.platformUid) {\n  await startReauthFlow(channelId)\n}\nconst profile = await authProvider.getProfile(credential)","handlingStrategy":"validation","validationCode":"if (!credential.platformUid) {\n  scheduleReauth(channelId)\n} else {\n  await authProvider.getProfile(credential)\n}","typeGuard":"function hasPlatformUid(c: unknown): c is { platformUid: string } {\n  return typeof (c as any)?.platformUid === 'string' && (c as any).platformUid.length > 0\n}","tryCatchPattern":"try {\n  const profile = await authProvider.getProfile(ctx)\n} catch (e) {\n  if (e?.code === 'ChannelAuthPlatformUidMissing') {\n    await startReauthFlow(ctx.channelId)\n    return\n  }\n  throw e\n}","preventionTips":["Store openid as platformUid during OAuth","Backfill platformUid for legacy credentials","Guard getProfile call sites with a uid check","Verify credential context builders map openid correctly"],"tags":["wechat","oauth","openid","missing-parameter","auth"],"backgroundTag":"missing-required-parameter","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}