yikart/AiToEarn · error · AppException

ChannelAuthPlatformUidMissing

ChannelAuthPlatformUidMissing

Error message

ResponseCode.ChannelAuthPlatformUidMissing

What it means

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.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/douyin/douyin-auth.provider.ts:116

    }

    const result = await this.douyinService.refreshAccessToken(input.refreshToken)

    return {
      accessToken: result.accessToken,
      refreshToken: result.refreshToken,
      expiresAt: result.expiresAt,
      scope: result.scope,
      tokenType: 'Bearer',
      raw: {
        refreshExpiresAt: result.refreshExpiresAt,
      },
    }
  }

  async revoke(input: RevokeCredentialInput): Promise<void> {
    if (!input.platformUid) {
      throw new AppException(ResponseCode.ChannelAuthPlatformUidMissing)
    }

    await this.douyinService.revokeAccessToken(input.accessToken, input.platformUid)
  }

  async getProfile(input: CredentialContext): Promise<PlatformAccountProfile> {
    if (!input.platformUid) {
      throw new AppException(ResponseCode.ChannelAuthPlatformUidMissing)
    }

    const userInfo = await this.douyinService.getUserInfo(input.accessToken, input.platformUid)

    return {
      platformUid: input.platformUid,
      displayName: userInfo.nickname ?? '',
      avatarUrl: userInfo.avatar,
      raw: {
        openId: userInfo.openId,

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Fetch the profile first (getProfile) to resolve platformUid, then call revoke with the complete input.
  2. Backfill platformUid on existing credential records from stored account profile data.
  3. If platformUid is unrecoverable, treat the token as orphaned: mark the channel disconnected locally and let the token expire.

Example fix

// before
await douyinAuth.revoke({ accessToken })
// after
const profile = await douyinAuth.getProfile({ accessToken, platformUid })
await douyinAuth.revoke({ accessToken, platformUid: profile.platformUid })
Defensive patterns

Strategy: validation

Validate before calling

if (!input.platformUid) {
  // resolve before revoke
  const profile = await douyinAuth.getProfile({ accessToken: input.accessToken, platformUid: storedUid })
  input = { ...input, platformUid: profile.platformUid }
}
await douyinAuth.revoke(input)

Type guard

function isRevocable(input: Partial<RevokeCredentialInput>): input is RevokeCredentialInput {
  return typeof input.accessToken === 'string' && typeof input.platformUid === 'string' && input.platformUid.length > 0
}

Try / catch

try {
  await douyinAuth.revoke(input)
} catch (e) {
  if (e.code === 'ChannelAuthPlatformUidMissing') {
    // token is orphaned; disconnect locally and let it expire
    await markChannelDisconnected(channelId)
  } else throw e
}

Prevention

When it happens

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

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

Related errors


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