{"record":{"id":"de551d39e531a3ab","repo":"santifer/career-ops","slug":"capacity-must-be-a-finite-number-1-got-capac","errorCode":null,"errorMessage":"capacity must be a finite number >= 1, got ${capacity}","messagePattern":"capacity must be a finite number >= 1, got (.+?)","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"providers/_dns-cache.mjs","lineNumber":139,"sourceCode":" * @param {number} [options.ratePerMin] - Sustained calls per minute. Must be > 0.\n * @param {number} [options.capacity] - Burst size, in tokens.\n * @param {() => number} [options.now] - Clock source, injectable for tests.\n * @param {(fn: Function, ms: number) => void} [options.setTimer] - Timer, injectable for tests.\n * @returns {{ take: (fn: Function) => void, pending: number, stats: () => { delayed: number, waitedMs: number } }}\n */\nexport function createTokenBucket(options = {}) {\n  const ratePerMin = options.ratePerMin ?? DEFAULT_LOOKUPS_PER_MIN;\n  const capacity = options.capacity ?? DEFAULT_BURST;\n  const now = options.now ?? Date.now;\n  const setTimer = options.setTimer ?? setTimeout;\n\n  // Caller-supplied, so validate rather than assert: a zero or negative rate\n  // would make the refill interval Infinity and hang every queued lookup.\n  if (!Number.isFinite(ratePerMin) || ratePerMin <= 0) {\n    throw new RangeError(`ratePerMin must be a finite number > 0, got ${ratePerMin}`);\n  }\n  if (!Number.isFinite(capacity) || capacity < 1) {\n    throw new RangeError(`capacity must be a finite number >= 1, got ${capacity}`);\n  }\n\n  const tokensPerMs = ratePerMin / 60_000;\n  let tokens = capacity;\n  let lastRefill = now();\n  /** @type {{ fn: Function, queuedAt: number }[]} */\n  const queue = [];\n  let timerPending = false;\n  let delayed = 0;\n  let waitedMs = 0;\n\n  function refill() {\n    const t = now();\n    tokens = Math.min(capacity, tokens + (t - lastRefill) * tokensPerMs);\n    lastRefill = t;\n  }\n\n  function schedule() {","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/providers/_dns-cache.mjs#L121-L157","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\ncreateTokenBucket(60, 0); // RangeError\n// after\ncreateTokenBucket(60, 10);","handlingStrategy":"validation","validationCode":"function toCapacity(raw, fallback = 10) {\n  const n = Math.floor(Number(raw));\n  return Number.isFinite(n) && n >= 1 ? n : fallback;\n}\nconst bucket = createTokenBucket(60, toCapacity(process.env.DNS_BURST));","typeGuard":"function isValidCapacity(n) {\n  return typeof n === 'number' && Number.isFinite(n) && n >= 1;\n}","tryCatchPattern":"try {\n  bucket = createTokenBucket(ratePerMin, capacity);\n} catch (err) {\n  if (err instanceof RangeError && err.message.includes('capacity')) {\n    console.warn(`Bad capacity ${capacity}, falling back to 10`);\n    bucket = createTokenBucket(ratePerMin, 10);\n  } else throw err;\n}","preventionTips":["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."],"tags":["configuration","validation","rate-limiting","range-error"],"backgroundTag":"invalid-argument-range","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}