{"record":{"id":"3bc1cd2449c11215","repo":"yikart/AiToEarn","slug":"channelauthplatformuidmissing","errorCode":"ChannelAuthPlatformUidMissing","errorMessage":"ResponseCode.ChannelAuthPlatformUidMissing","messagePattern":"ResponseCode\\.ChannelAuthPlatformUidMissing","errorType":"exception","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/douyin/douyin-auth.provider.ts","lineNumber":116,"sourceCode":"    }\n\n    const result = await this.douyinService.refreshAccessToken(input.refreshToken)\n\n    return {\n      accessToken: result.accessToken,\n      refreshToken: result.refreshToken,\n      expiresAt: result.expiresAt,\n      scope: result.scope,\n      tokenType: 'Bearer',\n      raw: {\n        refreshExpiresAt: result.refreshExpiresAt,\n      },\n    }\n  }\n\n  async revoke(input: RevokeCredentialInput): Promise<void> {\n    if (!input.platformUid) {\n      throw new AppException(ResponseCode.ChannelAuthPlatformUidMissing)\n    }\n\n    await this.douyinService.revokeAccessToken(input.accessToken, input.platformUid)\n  }\n\n  async getProfile(input: CredentialContext): Promise<PlatformAccountProfile> {\n    if (!input.platformUid) {\n      throw new AppException(ResponseCode.ChannelAuthPlatformUidMissing)\n    }\n\n    const userInfo = await this.douyinService.getUserInfo(input.accessToken, input.platformUid)\n\n    return {\n      platformUid: input.platformUid,\n      displayName: userInfo.nickname ?? '',\n      avatarUrl: userInfo.avatar,\n      raw: {\n        openId: userInfo.openId,","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/douyin/douyin-auth.provider.ts#L98-L134","documentation":"DouyinAuthProvider.revoke() needs the platform user id (platformUid) to revoke the Douyin access token for that specific open_id. If input.platformUid is absent, the provider throws ChannelAuthPlatformUidMissing before making any network call, because Douyin's revoke API is per-open_id.","triggerScenarios":"Calling revoke (e.g. disconnecting a Douyin channel or admin cleanup) with a RevokeCredentialInput that lacks platformUid — the credential record never stored the platform user id.","commonSituations":"Legacy channel rows created before platformUid was persisted, credentials built from a flow that skipped profile fetching, or callers constructing the revoke input manually from partial data.","solutions":["Fetch the profile first (getProfile) to resolve platformUid, then call revoke with the complete input.","Backfill platformUid on existing credential records from stored account profile data.","If platformUid is unrecoverable, treat the token as orphaned: mark the channel disconnected locally and let the token expire."],"exampleFix":"// before\nawait douyinAuth.revoke({ accessToken })\n// after\nconst profile = await douyinAuth.getProfile({ accessToken, platformUid })\nawait douyinAuth.revoke({ accessToken, platformUid: profile.platformUid })","handlingStrategy":"validation","validationCode":"if (!input.platformUid) {\n  // resolve before revoke\n  const profile = await douyinAuth.getProfile({ accessToken: input.accessToken, platformUid: storedUid })\n  input = { ...input, platformUid: profile.platformUid }\n}\nawait douyinAuth.revoke(input)","typeGuard":"function isRevocable(input: Partial<RevokeCredentialInput>): input is RevokeCredentialInput {\n  return typeof input.accessToken === 'string' && typeof input.platformUid === 'string' && input.platformUid.length > 0\n}","tryCatchPattern":"try {\n  await douyinAuth.revoke(input)\n} catch (e) {\n  if (e.code === 'ChannelAuthPlatformUidMissing') {\n    // token is orphaned; disconnect locally and let it expire\n    await markChannelDisconnected(channelId)\n  } else throw e\n}","preventionTips":["Store platformUid on the credential at connect time, next to the token.","Never construct revoke inputs by hand from partial records — load the full credential.","Treat missing platformUid as an unrecoverable credential and flag for re-auth."],"tags":["douyin","oauth","revoke","missing-argument"],"backgroundTag":"missing-platform-uid","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}