yikart/AiToEarn · error · AppException

ResponseCode.ChannelAuthSelectedAccountUnavailable

ResponseCode.ChannelAuthSelectedAccountUnavailable

Error message

ChannelAuthSelectedAccountUnavailable

What it means

saveSelectedAccountsForUser validates every account the user selected against the selectableAccounts list captured during the auth flow. If a selected account's key is not present in that list, the server rejects the save because the account was never offered/available for selection. This guards against stale or forged selections.

Source

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

    selectedAccounts: SelectedAccountIdentity[]
    groupId?: string
    source: string
    allowReassign?: boolean
  }): Promise<{ accountIds: string[], accounts: ConnectedSelectableAccount[] }> {
    if (input.selectedAccounts.length === 0) {
      throw new AppException(ResponseCode.ChannelAuthSelectionRequired)
    }

    const keyOf = (account: SelectedAccountIdentity) => `${account.platformUid}\u0000${account.account ?? ''}`
    const selectedAccounts = Array.from(
      new Map(input.selectedAccounts.map(account => [keyOf(account), account])).values(),
    )
    const selectableAccounts = new Map(
      input.selectableAccounts.map(account => [keyOf(account), account]),
    )
    const unknownAccount = selectedAccounts.find(account => !selectableAccounts.has(keyOf(account)))
    if (unknownAccount) {
      throw new AppException(ResponseCode.ChannelAuthSelectedAccountUnavailable)
    }

    const groupId = await this.resolveGroupId(input.userId, input.groupId)
    const accountIds: string[] = []
    const accounts: ConnectedSelectableAccount[] = []

    for (const selectedAccount of selectedAccounts) {
      const selectable = selectableAccounts.get(keyOf(selectedAccount))
      if (!selectable) {
        throw new AppException(ResponseCode.ChannelAuthSelectedAccountUnavailable)
      }

      const platform = selectable.platform ?? input.platform
      const account = await this.createOrUpdateAccount({
        userId: input.userId,
        platform,
        platformUid: selectable.platformUid,
        account: selectable.account,

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Refresh the selectable accounts list from the auth flow and resubmit only accounts present in it
  2. Clear stale client-side selected-account state before retrying the save
  3. Log the offending key and compare with selectableAccounts to find the mismatch source
  4. Verify the client is not submitting selections from an older auth session/token

Example fix

// before
const selected = staleCache.accounts // may contain accounts from an old session
await api.saveSelectedAccounts({ ...input, selectedAccounts: selected })
// after
const selectableKeys = new Set(input.selectableAccounts.map(keyOf))
const selected = staleCache.accounts.filter(a => selectableKeys.has(keyOf(a)))
await api.saveSelectedAccounts({ ...input, selectedAccounts: selected })
Defensive patterns

Strategy: validation

Validate before calling

const selectableKeys = new Set(input.selectableAccounts.map(keyOf))
const valid = selectedAccounts.every(a => selectableKeys.has(keyOf(a)))
if (!valid) throw new Error('Refusing to save: some selected accounts are not selectable')

Type guard

function isSelectable(a, list) { return list.some(s => keyOf(s) === keyOf(a)) }

Try / catch

try {
  await api.saveSelectedAccounts(input)
} catch (e) {
  if (e.code === 'ChannelAuthSelectedAccountUnavailable') {
    await refreshSelectableAccounts(); return retryWithValidSelections()
  }
  throw e
}

Prevention

When it happens

Trigger: Calling saveSelectedAccountsForUser with selectedAccounts containing an entry whose keyOf(...) value does not exist in input.selectableAccounts — e.g. the client submits an account id from a previous auth session, or the selectable list was rebuilt without that account.

Common situations: Frontend caches selected accounts across re-auth attempts; user completes auth on one device and a stale client submits old selections; the channel provider returned a different account set than expected; race where the selectable list was refreshed between render and submit.

Related errors


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