yikart/AiToEarn · error · AppException

ChannelAccountAlreadyConnectedToAnotherUser

ChannelAccountAlreadyConnectedToAnotherUser

Error message

ResponseCode.ChannelAccountAlreadyConnectedToAnotherUser

What it means

createOrUpdateAccount throws ChannelAccountAlreadyConnectedToAnotherUser (AppException, ResponseCode.ChannelAccountAlreadyConnectedToAnotherUser) when the platform identity already exists but belongs to a different userId, and the update path was skipped (no allowReassign) so account.userId !== input.userId. This is an ownership conflict guard preventing one user from hijacking a channel account connected by another user. A warning is logged with the input and existing account before throwing.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts:752

    let account = await this.accountRepo.getByIdentity(identity)
    let created = false
    if (!account) {
      account = await this.accountRepo.createByIdentity(identity, accountData)
      created = true
    }
    if (!created && account && (account.userId === input.userId || !account.userId || input.allowReassign)) {
      account = await this.accountRepo.updateByIdentity(identity, accountData) ?? account
    }

    if (!account) {
      throw new AppException(ResponseCode.AccountCreateFailed)
    }
    if (account.userId !== input.userId) {
      this.logger.warn(
        { input, existingAccount: account },
        'Channel account already connected to another user',
      )
      throw new AppException(ResponseCode.ChannelAccountAlreadyConnectedToAnotherUser)
    }

    return account
  }

  private async saveSelectableCredential(
    accountId: string,
    platform: AccountType,
    credential: PlatformAccountCredentialSnapshot,
  ): Promise<void> {
    await this.credentialService.saveCredential(accountId, platform, {
      accessToken: credential.accessToken,
      refreshToken: credential.refreshToken,
      expiresAt: credential.expiresAt,
      scope: credential.scope,
    })
  }

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. If reassignment is intended, set allowReassign=true in the auth input so the existing account is transferred to the new user.
  2. Have the current owner disconnect the account first, then reconnect under the new user.
  3. Use a different platform account for the new user, or accept the error and surface 'account already connected to another user' in the UI.
  4. Verify which userId owns the identity (query by identity) before initiating the OAuth flow to avoid surprise conflicts.

Example fix

// before
await authService.handleAuthCallback({ userId, platform, ... }) // throws if owned by other user
// after
await authService.handleAuthCallback({ userId, platform, allowReassign: true, ... })
Defensive patterns

Strategy: try-catch

Validate before calling

const existing = await accountRepo.getByIdentity({ type: platform, uid: platformUid, account })
if (existing && existing.userId && existing.userId !== userId && !allowReassign) {
  throw new AppException(ResponseCode.ChannelAccountAlreadyConnectedToAnotherUser)
}

Type guard

function ownedByUser(a: { userId?: string } | null, userId: string): boolean {
  return !!a && (!a.userId || a.userId === userId)
}

Try / catch

try {
  await authService.handleAuthCallback(input)
} catch (e) {
  if (e instanceof AppException && e.code === ResponseCode.ChannelAccountAlreadyConnectedToAnotherUser) {
    return res.status(409).json({ message: 'This channel account is already connected to another user' })
  }
  throw e
}

Prevention

When it happens

Trigger: Two AiToEarn users authorizing the same platform account (same platform+uid); re-running OAuth for a channel previously connected by a teammate without allowReassign=true; YouTube accounts distinguished by handle where the same channel maps to an existing row owned elsewhere.

Common situations: Agency accounts (company TikTok/YouTube) connected by employee A, then employee B tries to connect the same one; staging data copied from prod retaining another user's ownership; testing with a colleague's platform account.

Related errors


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