santifer/career-ops · error · RangeError
capacity must be a finite number >= 1, got ${capacity}
Error message
capacity must be a finite number >= 1, got ${capacity} What it means
The companion validation in createTokenBucket(): capacity must be a finite number >= 1 because it initializes the token count and burst size. Zero or negative capacity would mean the bucket can never hold a token, deadlocking all queued lookups; non-finite values break the token math.
Source
Thrown at providers/_dns-cache.mjs:139
* @param {number} [options.ratePerMin] - Sustained calls per minute. Must be > 0.
* @param {number} [options.capacity] - Burst size, in tokens.
* @param {() => number} [options.now] - Clock source, injectable for tests.
* @param {(fn: Function, ms: number) => void} [options.setTimer] - Timer, injectable for tests.
* @returns {{ take: (fn: Function) => void, pending: number, stats: () => { delayed: number, waitedMs: number } }}
*/
export function createTokenBucket(options = {}) {
const ratePerMin = options.ratePerMin ?? DEFAULT_LOOKUPS_PER_MIN;
const capacity = options.capacity ?? DEFAULT_BURST;
const now = options.now ?? Date.now;
const setTimer = options.setTimer ?? setTimeout;
// Caller-supplied, so validate rather than assert: a zero or negative rate
// would make the refill interval Infinity and hang every queued lookup.
if (!Number.isFinite(ratePerMin) || ratePerMin <= 0) {
throw new RangeError(`ratePerMin must be a finite number > 0, got ${ratePerMin}`);
}
if (!Number.isFinite(capacity) || capacity < 1) {
throw new RangeError(`capacity must be a finite number >= 1, got ${capacity}`);
}
const tokensPerMs = ratePerMin / 60_000;
let tokens = capacity;
let lastRefill = now();
/** @type {{ fn: Function, queuedAt: number }[]} */
const queue = [];
let timerPending = false;
let delayed = 0;
let waitedMs = 0;
function refill() {
const t = now();
tokens = Math.min(capacity, tokens + (t - lastRefill) * tokensPerMs);
lastRefill = t;
}
function schedule() {View on GitHub (pinned to 1696bec4d0)
Solutions
- Pass a finite integer >= 1 (a common value is equal to ratePerMin for one full minute of burst).
- Fix the config/env value feeding capacity; default it explicitly: capacity = Number(cfg.burst ?? 10).
- Don't use 0 to disable limiting — increase the rate instead or skip bucket creation entirely.
- Clamp before calling: capacity = Math.max(1, Math.floor(rawCapacity)) after checking Number.isFinite(rawCapacity).
Example fix
// before createTokenBucket(60, 0); // RangeError // after createTokenBucket(60, 10);
Defensive patterns
Strategy: validation
Validate before calling
function toCapacity(raw, fallback = 10) {
const n = Math.floor(Number(raw));
return Number.isFinite(n) && n >= 1 ? n : fallback;
}
const bucket = createTokenBucket(60, toCapacity(process.env.DNS_BURST)); Type guard
function isValidCapacity(n) {
return typeof n === 'number' && Number.isFinite(n) && n >= 1;
} Try / catch
try {
bucket = createTokenBucket(ratePerMin, capacity);
} catch (err) {
if (err instanceof RangeError && err.message.includes('capacity')) {
console.warn(`Bad capacity ${capacity}, falling back to 10`);
bucket = createTokenBucket(ratePerMin, 10);
} else throw err;
} Prevention
- Clamp capacity with Math.max(1, Math.floor(n)) after a Number.isFinite check, before calling the factory.
- Never pass 0 as burst/capacity — the minimum is 1 by design.
- Default burst env vars explicitly instead of letting undefined become NaN.
- Pair capacity >= ratePerMin/60*burst-window when tuning so the bucket can actually refill.
When it happens
Trigger: Calling createTokenBucket(rate, capacity) with 0, a negative value, NaN, or Infinity — e.g. a burst config of '0' meant to disable bursting, or capacity derived from an unset env var.
Common situations: Config where burst/capacity was left blank or set to 0 believing it disables the limiter; NaN from parsing a malformed number; an off-by-one where a minimum of 1 is required; copy-pasted tuning values.
Related errors
- ratePerMin must be a finite number > 0, got ${ratePerMin}
- gmail: invalid days_back "${ctx?.settings?.days_back}" (must
- arbeitsagentur: entry "${entry.name || '(unnamed)'}" has no
- ashby: invalid URL: ${url}
- ashby: URL must use HTTPS: ${url}
AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01).
Data as JSON: /api/errors/de551d39e531a3ab.
Report an issue: GitHub.