{"record":{"id":"2c7f9e5f3f9ed24c","repo":"decolua/9router","slug":"all-accounts-unavailable","errorCode":null,"errorMessage":"All accounts unavailable","messagePattern":"All accounts unavailable","errorType":"http","errorClass":null,"httpStatus":503,"severity":"error","filePath":"src/sse/handlers/chat.js","lineNumber":243,"sourceCode":"  let lastError = null;\n  let lastStatus = null;\n\n  while (true) {\n    const credentials = await getProviderCredentials(provider, excludeConnectionIds, model);\n\n    // All accounts unavailable\n    if (!credentials || credentials.allRateLimited) {\n      if (credentials?.allRateLimited) {\n        const errorMsg = lastError || credentials.lastError || \"Unavailable\";\n        const status = lastStatus || Number(credentials.lastErrorCode) || HTTP_STATUS.SERVICE_UNAVAILABLE;\n        log.warn(\"CHAT\", `[${provider}/${model}] ${errorMsg} (${credentials.retryAfterHuman})`);\n        return unavailableResponse(status, `[${provider}/${model}] ${errorMsg}`, credentials.retryAfter, credentials.retryAfterHuman);\n      }\n      if (excludeConnectionIds.size === 0) {\n        log.warn(\"AUTH\", `No active credentials for provider: ${provider}`);\n        return errorResponse(HTTP_STATUS.NOT_FOUND, `No active credentials for provider: ${provider}`);\n      }\n      log.warn(\"CHAT\", \"No more accounts available\", { provider });\n      return errorResponse(lastStatus || HTTP_STATUS.SERVICE_UNAVAILABLE, lastError || \"All accounts unavailable\");\n    }\n\n    // Account selection shown in the unified \"▶\" line (acc:...)\n    const refreshedCredentials = await checkAndRefreshToken(provider, credentials);\n\n    // Ensure real project ID is available for providers that need it (P0 fix: cold miss)\n    if ((provider === \"antigravity\" || provider === \"gemini-cli\") && !refreshedCredentials.projectId) {\n      const pid = await getProjectIdForConnection(credentials.connectionId, refreshedCredentials.accessToken, provider);\n      if (pid) {\n        refreshedCredentials.projectId = pid;\n        // Persist to DB in background so subsequent requests have it immediately\n        updateProviderCredentials(credentials.connectionId, { projectId: pid }).catch(() => { });\n      }\n    }\n\n    // Use shared chatCore\n    const chatSettings = await getSettings();","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/sse/handlers/chat.js#L225-L261","documentation":"There were credentials for the provider, but the account-selection loop exhausted them all (excludeConnectionIds non-empty) without finding a usable one. The handler returns lastStatus (or 503) with lastError, defaulting the message to 'All accounts unavailable'.","triggerScenarios":"POST /v1/chat/completions where each of the provider's accounts was tried and excluded during this request (auth failures, per-account errors), and after the loop none remain, so the last upstream error/status is returned.","commonSituations":"Multiple accounts all with expired/invalid API keys; OAuth refresh tokens revoked on every account; per-account upstream errors (quota, suspension) across the whole pool; a combo whose member models map to providers that are all down.","solutions":["Check the dashboard log for lastError to see why each account failed; fix the root cause per account (re-auth, new key).","Re-authenticate OAuth accounts whose tokens expired/revoked; replace invalid API keys.","Add a healthy provider account or configure combo fallback to other providers.","If an account was marked unavailable, clear its error state (clearAccountError) after fixing credentials and retry."],"exampleFix":"// before\n// all provider accounts invalid; client blindly retries the same model\n{ \"model\": \"cursor/gpt-4o\" }\n\n// after\n// fix/re-auth accounts in dashboard, or add fallback combo\n{ \"model\": \"my-combo\" }  // combo: cursor/gpt-4o, openai/gpt-4o","handlingStrategy":"fallback","validationCode":"const creds = await fetch(`${base}/dashboard/api/credentials`).then(r => r.json());\nconst healthy = creds.filter(c => c.provider === provider && c.active && !c.lastError);\nif (healthy.length === 0) throw new Error(`all ${provider} accounts unhealthy; fix or use another provider`);","typeGuard":"null","tryCatchPattern":"const res = await fetch(url, opts);\nconst text = await res.text();\nif (res.status >= 500 && text.includes('All accounts unavailable')) {\n  // fail over to another provider/model rather than retrying the same one\n  return requestVia(fallbackModel, request);\n}","preventionTips":["Monitor per-account lastError in the dashboard and fix root causes promptly.","Keep at least one healthy account per critical provider.","Use combos with cross-provider fallback.","Alert on repeated 'No more accounts available' log lines."],"tags":["upstream","credentials","fallback","provider"],"backgroundTag":"all-accounts-unavailable","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}