{"record":{"id":"ca356b92689c09bb","repo":"stablyai/orca","slug":"history-limit-must-be-a-positive-integer-got-li","errorCode":null,"errorMessage":"History limit must be a positive integer, got ${limit}","messagePattern":"History limit must be a positive integer, got (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"config/scripts/live-freeze-bounded-history.mjs","lineNumber":9,"sourceCode":"export class BoundedLiveFreezeHistory {\n  #entries = []\n  #limit\n  #nextIndex = 0\n  #totalCount = 0\n\n  constructor(limit) {\n    if (!Number.isInteger(limit) || limit <= 0) {\n      throw new Error(`History limit must be a positive integer, got ${limit}`)\n    }\n    this.#limit = limit\n  }\n\n  add(entry) {\n    this.#totalCount += 1\n    if (this.#entries.length < this.#limit) {\n      this.#entries.push(entry)\n      return\n    }\n    this.#entries[this.#nextIndex] = entry\n    this.#nextIndex = (this.#nextIndex + 1) % this.#limit\n  }\n\n  get retainedCount() {\n    return this.#entries.length\n  }\n","sourceCodeStart":1,"sourceCodeEnd":27,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/live-freeze-bounded-history.mjs#L1-L27","documentation":"BoundedLiveFreezeHistory is a fixed-capacity ring buffer that records live-freeze timing entries. The constructor enforces that 'limit' be a positive integer because the buffer uses it both as the array length cap and as the modulus in the wraparound index (#nextIndex = (next + 1) % limit). A non-integer, zero, or negative value would corrupt the modulus math or never allow insertion. The throw protects the invariant that add() can always overwrite in O(1).","triggerScenarios":"Constructing the history with new BoundedLiveFreezeHistory(limit) where limit is 0, negative, a float like 2.5, a numeric string like \"100\", NaN, or undefined. The check Number.isInteger(limit) && limit > 0 is what fails.","commonSituations":"Reading the capacity from an env var or CLI flag without coercion (e.g. passing a string), computing it dynamically and getting 0 on an empty result set, or passing Math.floor of a value that is already corrupt. In the freeze repro scripts the limit is often a constant like 100, but a misconfigured env override can inject a bad value.","solutions":["Pass a hardcoded positive integer literal (e.g. new BoundedLiveFreezeHistory(100)) when the capacity is a fixed sampling size.","If the limit comes from env/config, coerce and clamp before construction: const limit = Math.max(1, Math.trunc(Number(raw) || 0)).","Add a runtime assertion in the caller that prints which input produced the bad limit so the source of the bad value is traceable."],"exampleFix":"// before\nconst history = new BoundedLiveFreezeHistory(readEnv('FREEZE_HISTORY_SIZE'))\n\n// after\nconst raw = Number(readEnv('FREEZE_HISTORY_SIZE'))\nif (!Number.isInteger(raw) || raw <= 0) {\n  throw new Error(`FREEZE_HISTORY_SIZE must be a positive integer, got ${raw}`)\n}\nconst history = new BoundedLiveFreezeHistory(raw)","handlingStrategy":"validation","validationCode":"function assertPositiveInt(limit, name = 'limit') {\n  if (!Number.isInteger(limit) || limit <= 0) {\n    throw new TypeError(`${name} must be a positive integer, got ${limit}`)\n  }\n}\n// caller:\nassertPositiveInt(rawLimit, 'FREEZE_HISTORY_SIZE')\nconst history = new BoundedLiveFreezeHistory(rawLimit)","typeGuard":"const isPositiveInteger = (n) => Number.isInteger(n) && n > 0","tryCatchPattern":"try {\n  history = new BoundedLiveFreezeHistory(limit)\n} catch (e) {\n  throw new Error(`Bad history config (${limit}): ${e.message}`)\n}","preventionTips":["Always coerce env-derived sizes with Number() + Math.trunc and clamp with Math.max(1, ...).","Keep ring-buffer capacity a constant literal when the sampling size is fixed.","Unit-test the constructor with 0, negative, float, string, NaN, undefined inputs."],"tags":["validation","constructor","ring-buffer","integer"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}