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
- Refresh the selectable accounts list from the auth flow and resubmit only accounts present in it
- Clear stale client-side selected-account state before retrying the save
- Log the offending key and compare with selectableAccounts to find the mismatch source
- 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
- Always derive selections from the current selectableAccounts response
- Clear stale selection state when restarting an auth flow
- Use a stable deterministic keyOf on both client and server
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
- No subtitle entries in response
- No response from Gemini
- Invalid subtitle data: ${z.prettifyError(result.error)}
- Canvas not provided and no vid:// video source found in Trac
- Canvas not provided and failed to retrieve video dimensions
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/bb3bdafea01032e8.
Report an issue: GitHub.