decolua/9router · error

All accounts unavailable

Error message

All accounts unavailable

What it means

After at least one account was tried and excluded (excludeConnectionIds non-empty), the loop ran out of accounts: getProviderCredentials returned nothing and not allRateLimited. The handler returns lastStatus (or 503) with lastError, defaulting the message to 'All accounts unavailable'. It means every credential for the provider failed and was excluded during this request.

Source

Thrown at src/sse/handlers/search.js:190

      credentials = await getProviderCredentials(fallbackProviderId, excludeConnectionIds, searchLockKey);
      if (credentials) {
        credentialProviderId = fallbackProviderId;
        log.info("AUTH", `\x1b[32m${providerId} reusing ${fallbackProviderId} credentials\x1b[0m`);
      }
    }

    if (!credentials || credentials.allRateLimited) {
      if (credentials?.allRateLimited) {
        const errorMsg = lastError || credentials.lastError || "Unavailable";
        const status = lastStatus || Number(credentials.lastErrorCode) || HTTP_STATUS.SERVICE_UNAVAILABLE;
        log.warn("SEARCH", `[${providerId}] ${errorMsg} (${credentials.retryAfterHuman})`);
        return unavailableResponse(status, `[${providerId}] ${errorMsg}`, credentials.retryAfter, credentials.retryAfterHuman);
      }
      if (excludeConnectionIds.size === 0) {
        log.error("AUTH", `No credentials for provider: ${providerId}`);
        return errorResponse(HTTP_STATUS.BAD_REQUEST, `No credentials for provider: ${providerId}`);
      }
      log.warn("SEARCH", "No more accounts available", { provider: providerId });
      return errorResponse(lastStatus || HTTP_STATUS.SERVICE_UNAVAILABLE, lastError || "All accounts unavailable");
    }

    log.info("AUTH", `\x1b[32mUsing ${providerId} account: ${credentials.connectionName}\x1b[0m`);

    const refreshedCredentials = await checkAndRefreshToken(providerId, credentials);

    const result = await handleSearchCore({
      body: coreBody,
      provider: resolvedProvider,
      providerConfig,
      credentials: refreshedCredentials,
      log,
      onCredentialsRefreshed: async (newCreds) => {
        await updateProviderCredentials(credentials.connectionId, {
          accessToken: newCreds.accessToken,
          refreshToken: newCreds.refreshToken,
          providerSpecificData: newCreds.providerSpecificData,

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Read the accompanying `lastError` in the response/log — it names why the first account failed.
  2. Re-authenticate or update credentials for the provider's connections in the dashboard.
  3. Verify upstream status: a provider-wide outage recovers on its own; retry with backoff.
  4. Add healthy accounts to the pool; remove dead connections that burn fallback attempts.
  5. Check token refresh logs (checkAndRefreshToken) for repeated refresh failures.
Defensive patterns

Strategy: fallback

Try / catch

try {
  return await searchWith(provider, body);
} catch (e) {
  if (e.status === 503 && /accounts unavailable/.test(e.message)) {
    return await searchWith(fallbackProvider, body); // different provider entirely
  }
  throw e;
}

Prevention

When it happens

Trigger: Multi-account providers where each account hit a fallback-eligible error (5xx, auth failure) in sequence during one request; lastError unset when the first failure carried no message, so the default text appears; concurrent requests exhausting the shared account pool.

Common situations: All OAuth tokens expired simultaneously (refresh failing); provider-wide outage returning 502/503 for every account; accounts disabled/invalid in the dashboard but still listed; one shared key used across many clients tripping per-account limits.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/9dc2c7840d9c22be. Report an issue: GitHub.