yikart/AiToEarn · error · AppException

ResponseCode.ChannelAccountAlreadyConnectedToAnotherUser

ResponseCode.ChannelAccountAlreadyConnectedToAnotherUser

Error message

ChannelAccountAlreadyConnectedToAnotherUser

What it means

After loading/creating the account, saveWritableAccount checks ownership: if the account belongs to a different userId, it throws ChannelAccountAlreadyConnectedToAnotherUser. The channel identity (platform uid) is already bound to another AiToEarn user, so the current user cannot claim it.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/accounts/account.service.ts:446

    identity: AccountIdentity,
    accountData: Partial<Account>,
    userId: string,
  ): Promise<Account> {
    let account = await this.accountRepository.getByIdentity(identity)
    let created = false
    if (!account) {
      account = await this.accountRepository.createByIdentity(identity, accountData)
      created = true
    }

    if (!account) {
      throw new AppException(ResponseCode.AccountCreateFailed)
    }
    if (!created && (account.userId === userId || !account.userId)) {
      account = await this.accountRepository.updateByIdentity(identity, accountData) ?? account
    }
    if (account.userId !== userId) {
      throw new AppException(ResponseCode.ChannelAccountAlreadyConnectedToAnotherUser)
    }

    return account
  }

  private async emitAccountConnected(userId: string, account: Account): Promise<void> {
    await this.eventStream.emit(
      EventStream.Channels,
      EventTopic.ChannelsAccountConnected,
      { userId, accountId: account.id, platform: account.type },
      { source: 'account-service' },
    )
  }

  private async normalizeCreateInput(data: AccountCreateInput): Promise<NormalizedAccountCreateInput> {
    if (data.type === AccountType.WeChatChannels) {
      return this.normalizeWeChatChannelsCreateInput(data)
    }

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Disconnect the channel account from the other user first (delete/unbind it), then reconnect.
  2. Log in as the user who owns the connection, or have that user release it.
  3. Use a different platform account for this user.
  4. If ownership transfer is required, use an admin flow to reassign account.userId rather than forcing the create call.

Example fix

// before
POST /channels/accounts { platform: 'twitter', uid: '123' } // bound to user A
// after
DELETE /channels/accounts/:id (as user A) then POST the same create as user B
Defensive patterns

Strategy: try-catch

Type guard

function isAlreadyConnected(e: unknown): boolean {
  return e instanceof AppException && e.code === ResponseCode.ChannelAccountAlreadyConnectedToAnotherUser
}

Try / catch

try {
  await accountService.createPluginAccount(userId, identity, data)
} catch (e) {
  if (e instanceof AppException && e.code === ResponseCode.ChannelAccountAlreadyConnectedToAnotherUser) {
    // surface UI message: 'This channel account is already linked to another user'
  } else throw e
}

Prevention

When it happens

Trigger: Calling createPluginAccount or createRelayAccountRecord with an account identity (platform + uid) whose stored account.userId differs from the requesting userId and the account already has an owner (created === false, account.userId set and different).

Common situations: Two team members connecting the same social media account; reconnecting after switching AiToEarn accounts without disconnecting first; test/staging accounts already claimed by another user.

Related errors


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