nodejs/node · error · BalancedPoolMissingUpstreamError
UND_ERR_BPL_MISSING_UPSTREAM
UND_ERR_BPL_MISSING_UPSTREAM
Error message
No upstream has been added to the BalancedPool
What it means
Thrown by BalancedPool's [kGetDispatcher] as BalancedPoolMissingUpstreamError (code UND_ERR_BPL_MISSING_UPSTREAM). The pool intentionally refuses to dispatch when this[kClients].length === 0 rather than queue a request that could wait forever for an upstream that may never arrive. So any dispatch before addUpstream has been called fails fast.
Source
Thrown at deps/undici/src/lib/dispatcher/balanced-pool.js:164
return this[kClients].find((pool) => (
pool[kUrl].origin === upstreamOrigin &&
pool.closed !== true &&
pool.destroyed !== true
))
}
get upstreams () {
return this[kClients]
.filter(dispatcher => dispatcher.closed !== true && dispatcher.destroyed !== true)
.map((p) => p[kUrl].origin)
}
[kGetDispatcher] () {
// We validate that pools is greater than 0,
// otherwise we would have to wait until an upstream
// is added, which might never happen.
if (this[kClients].length === 0) {
throw new BalancedPoolMissingUpstreamError()
}
let counter = 0
let maxWeightIndex = -1
while (counter++ < this[kClients].length) {
this[kIndex] = (this[kIndex] + 1) % this[kClients].length
const pool = this[kClients][this[kIndex]]
// decrease the current weight every `this[kClients].length`.
if (this[kIndex] === 0) {
// Set the current weight to the next lower weight.
this[kCurrentWeight] = this[kCurrentWeight] - this[kGreatestCommonDivisor]
if (this[kCurrentWeight] <= 0) {
this[kCurrentWeight] = this[kMaxWeightPerServer]
}View on GitHub (pinned to 1b2de5e052)
Solutions
- Add at least one upstream before dispatching: pool.addUpstream('https://upstream1.example.com').
- Pass upstreams in the constructor: new BalancedPool(['https://a.com', 'https://b.com']).
- Re-check pool.upstreams.length before dispatching when the pool is reconfigured dynamically.
Example fix
// before
const pool = new BalancedPool()
await pool.request({ method: 'GET', path: '/' }) // throws
// after
const pool = new BalancedPool(['https://a.com', 'https://b.com'])
await pool.request({ method: 'GET', path: '/' }) Defensive patterns
Strategy: validation
Validate before calling
function safeRequest(pool, opts) {
if (pool.upstreams.length === 0) throw new Error('BalancedPool has no upstreams')
return pool.request(opts)
} Type guard
const hasUpstreams = (pool) => Array.isArray(pool.upstreams) && pool.upstreams.length > 0
Try / catch
try {
await pool.request(opts)
} catch (e) {
if (e.code === 'UND_ERR_BPL_MISSING_UPSTREAM') { pool.addUpstream(DEFAULT_UPSTREAM); /* retry */ }
else throw e
} Prevention
- Always construct BalancedPool with at least one upstream or call addUpstream before dispatch.
- Check pool.upstreams.length during dynamic reconfiguration.
When it happens
Trigger: new BalancedPool() with no upstreams, then pool.request(...) or pool.dispatch(...); or all upstreams were removed/closed leaving kClients empty.
Common situations: Constructing a BalancedPool with an empty array and forgetting to call addUpstream; a race where the pool is used before the bootstrap that populates it runs; dynamic reconfiguration that removes every upstream.
Related errors
- UND_ERR_INVALID_ARG
- UND_ERR_INVALID_ARG
- UND_ERR_INVALID_ARG
- UND_ERR_INVALID_ARG
- UND_ERR_INVALID_RETURN_VALUE
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/358cea3082ce3b7c.
Report an issue: GitHub.