{"record":{"id":"1e7664e7d03f82c5","repo":"badges/shields","slug":"unable-to-select-next-github-token-from-pool","errorCode":null,"errorMessage":"Unable to select next GitHub token from pool","messagePattern":"Unable to select next GitHub token from pool","errorType":"exception","errorClass":"ImproperlyConfigured","httpStatus":500,"severity":"critical","filePath":"services/github/github-api-provider.js","lineNumber":221,"sourceCode":"        )})`,\n      )\n      pool.endBatchFor(token)\n    }\n  }\n\n  async fetch(requestFetcher, url, options = {}) {\n    const { baseUrl } = this\n\n    let token\n    let tokenString\n    let pool\n    if (this.authType === this.constructor.AUTH_TYPES.TOKEN_POOL) {\n      pool = this.poolForUrl(url)\n      try {\n        token = pool.next()\n      } catch (e) {\n        log.error(e)\n        throw new ImproperlyConfigured({\n          prettyMessage: 'Unable to select next GitHub token from pool',\n        })\n      }\n      tokenString = token.id\n    } else if (this.authType === this.constructor.AUTH_TYPES.GLOBAL_TOKEN) {\n      tokenString = this.globalToken\n    }\n\n    const mergedOptions = {\n      ...options,\n      ...{\n        headers: {\n          'User-Agent': userAgent,\n          'X-GitHub-Api-Version': this.restApiVersion,\n          ...options.headers,\n        },\n      },\n    }","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/badges/shields/blob/766fd8bc89a90b8534dc573ab72dec30215ab1ec/services/github/github-api-provider.js#L203-L239","documentation":"Thrown by GithubApiProvider.fetch when running in TOKEN_POOL auth mode and pool.next() throws — i.e. the token pool could not supply any usable GitHub token (all tokens exhausted/blocked/uninitialized). The provider wraps it as ImproperlyConfigured 'Unable to select next GitHub token from pool', indicating a server-side configuration problem rather than a client error.","triggerScenarios":"All tokens in the configured pool have hit rate limits or been invalidated so the pool's rotation throws; the pool was constructed with an empty/invalid token list in the badge server's configuration.","commonSituations":"Self-hosted shields.io deployments with GH_TOKEN_POOL unset, empty, or containing only revoked tokens; heavy usage draining all pooled tokens' rate limits; misencoded pool env var (wrong separator) yielding no valid tokens.","solutions":["Check and fix the token pool configuration (e.g. GH_TOKEN_POOL env var) ensuring valid, comma-separated tokens","Verify each token's validity with curl -H \"Authorization: token <t>\" https://api.github.com","Replace tokens that have expired or been revoked; generate new personal access tokens","Wait for GitHub rate limits to reset (tokens restore hourly) and retry","If only one token is needed, switch config to global token mode"],"exampleFix":"// before\nGH_TOKEN_POOL=\n// after\nGH_TOKEN_POOL=ghp_token1,ghp_token2,ghp_token3","handlingStrategy":"retry","validationCode":"const tokens = (process.env.GH_TOKEN_POOL || '').split(',').filter(t => t.length > 0);if (tokens.length === 0) { throw new Error('GH_TOKEN_POOL is empty; add at least one valid GitHub token'); }for (const t of tokens) { const r = await fetch('https://api.github.com/rate_limit', { headers: { Authorization: `token ${t}` } }); if (!r.ok) console.warn('token invalid:', t.slice(0, 8)); }","typeGuard":"const hasUsableTokens = (tokens) => Array.isArray(tokens) && tokens.every(t => typeof t === 'string' && t.startsWith('ghp_') || typeof t === 'string' && t.startsWith('github_pat_'));","tryCatchPattern":"try { return await githubApi.fetch(url); } catch (e) { if (String(e.message).includes('next GitHub token')) { await sleep(60_000); return retryWithBackoff(() => githubApi.fetch(url)); } throw e; }","preventionTips":["Configure multiple valid tokens in the token pool for self-hosted deployments","Rotate tokens before they expire and remove revoked ones","Monitor GitHub rate limits per token and alert when exhausted","Validate the pool env var format (comma-separated, no empty entries) at server startup"],"tags":["github","configuration","token-pool","rate-limit"],"backgroundTag":"missing-env-var","analyzedSha":"766fd8bc89a90b8534dc573ab72dec30215ab1ec","analyzedAt":"2026-08-30T01:40:27.499Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}