{"record":{"id":"d64e2d1f54cf701c","repo":"yikart/AiToEarn","slug":"responsecode-channelauthselectableaccountsnotfound","errorCode":"ResponseCode.ChannelAuthSelectableAccountsNotFound","errorMessage":"ChannelAuthSelectableAccountsNotFound","messagePattern":"ChannelAuthSelectableAccountsNotFound","errorType":"error_code","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts","lineNumber":215,"sourceCode":"\n    return {\n      accountId,\n      connectedAccounts: session.accounts,\n      callbackResponseType: credentialResult.callbackResponseType,\n      ...this.getAuthViewFields(session),\n    }\n  }\n\n  async connectSelectableAccounts(\n    sessionId: string,\n    selectedAccounts: SelectedAccountIdentity[],\n  ): Promise<ConnectSelectableAccountsResult> {\n    const session = await this.redis.getChannelAuthSession<AuthSession>(sessionId)\n    if (!this.isAccountAuthSessionRecord(session) || this.isSessionExpired(session) || session.status !== ChannelAuthSessionStatus.Pending) {\n      throw new AppException(ResponseCode.ChannelAuthSessionInvalid)\n    }\n    if (!session.selectableAccounts) {\n      throw new AppException(ResponseCode.ChannelAuthSelectableAccountsNotFound)\n    }\n\n    const result = await this.connectSelectedAccountsForUser({\n      userId: session.userId,\n      platform: session.platform,\n      selectableAccounts: session.selectableAccounts,\n      selectedAccounts,\n      groupId: session.groupId,\n      source: 'auth',\n      allowReassign: true,\n    })\n\n    session.status = ChannelAuthSessionStatus.Completed\n    session.accountId = result.accountIds[0]\n    session.accountIds = result.accountIds\n    session.accounts = result.accounts\n    delete session.rootCredentialId\n    delete session.selectableAccounts","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts#L197-L233","documentation":"Thrown in AuthService.connectSelectableAccounts when the session is valid and pending but has no selectableAccounts stored. selectableAccounts are only written to the session during completeCallback when the provider returns multiple candidate accounts; without them there is nothing to select from.","triggerScenarios":"submitSelections is called for a session whose completeCallback path connected a single account directly (no selection step stored), or a fabricated/manual POST to /accounts/auth/selections without having gone through the multi-account callback.","commonSituations":"Hitting the selections endpoint out of order (skipping the callback); a provider that returns one account so the selection branch never populated the session; stale session from an older flow version before selectableAccounts existed.","solutions":["Complete the provider callback first — selection is only available when the callback returned requiresSelection: true.","Verify the provider adapter actually returns selectableAccounts (listSelectableAccounts) for multi-account platforms.","Restart the auth flow if the session predates a code change or was created without the selection branch.","In the frontend, only render/submit the selection form when the callback response had requiresSelection set."],"exampleFix":"// before: submitting selections without requiresSelection\nawait post('/accounts/auth/selections', { accounts }) // any pending session\n\n// after: gate on the callback result\nif (callbackResult.requiresSelection) {\n  await post('/accounts/auth/selections', { accounts: selected })\n}","handlingStrategy":"validation","validationCode":"const callback = await completeCallback(...) // earlier step\nif (!callback.requiresSelection) {\n  throw new Error('No selectable accounts for this session; account already connected')\n}\n// only then POST /accounts/auth/selections","typeGuard":"function hasSelectableAccounts(s: AuthSession): s is AuthSession & { selectableAccounts: PlatformSelectableAccount[] } {\n  return Array.isArray(s.selectableAccounts) && s.selectableAccounts.length > 0\n}","tryCatchPattern":"try {\n  await connectSelectableAccounts(sessionId, selected)\n}\ncatch (e) {\n  if (getErrorCode(e) === ResponseCode.ChannelAuthSelectableAccountsNotFound) {\n    showFlowOrderError('Complete the provider callback before selecting accounts')\n  }\n}","preventionTips":["Gate the selection UI on requiresSelection from the callback response","Complete callbacks before allowing selection submissions","Test multi-account providers to confirm selectableAccounts is populated","Version/bump sessions after flow logic changes"],"tags":["oauth","selection","session","flow-order"],"backgroundTag":"oauth-selectable-accounts-missing","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}