{"record":{"id":"a7c66708db9e4790","repo":"can1357/oh-my-pi","slug":"name-must-be-a-non-negative-integer","errorCode":null,"errorMessage":"${name} must be a non-negative integer","messagePattern":"(.+?) must be a non-negative integer","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/lru.ts","lineNumber":32,"sourceCode":"\t/** Calculates an entry's size. */\n\tsizeCalculation?: (value: V, key: K) => number;\n\t/** Entry lifetime in milliseconds; zero disables expiry. */\n\tttl?: number;\n\t/** Refreshes an entry's lifetime when it is read. */\n\tupdateAgeOnGet?: boolean;\n\t/** Called synchronously before an entry is removed. */\n\tdispose?: (value: V, key: K, reason: DisposeReason) => void;\n}\n\ninterface Entry<V> {\n\tvalue: V;\n\tsize: number;\n\tstart: number;\n}\n\nfunction positiveInteger(value: number | undefined, name: string): number {\n\tif (value === undefined) return 0;\n\tif (!Number.isInteger(value) || value < 0) throw new TypeError(`${name} must be a non-negative integer`);\n\treturn value;\n}\n\n/** A bounded least-recently-used cache with optional size and lifetime limits. */\nexport class LRUCache<K, V> {\n\treadonly #entries = new Map<K, Entry<V>>();\n\treadonly #max: number;\n\treadonly #maxSize: number;\n\treadonly #maxEntrySize: number;\n\treadonly #sizeCalculation: ((value: V, key: K) => number) | undefined;\n\treadonly #ttl: number;\n\treadonly #updateAgeOnGet: boolean;\n\treadonly #dispose: ((value: V, key: K, reason: DisposeReason) => void) | undefined;\n\t#calculatedSize = 0;\n\n\t/** Creates an empty cache. */\n\tconstructor(options: LRUCacheOptions<K, V>) {\n\t\tthis.#max = positiveInteger(options.max, \"max\");","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/lru.ts#L14-L50","documentation":"LRUCache validates its numeric options (max, maxSize, maxEntrySize, ttl) through the positiveInteger helper: values must be undefined (treated as 0/disabled) or a non-negative integer. Passing a negative number, a fraction, NaN, Infinity, or a non-number type throws this TypeError naming the offending option.","triggerScenarios":"new LRUCache({ max: -1 }), new LRUCache({ max: 10.5 }), new LRUCache({ ttl: Number.NaN }), new LRUCache({ max: '100' }) (string), or calling setter/option paths like explicitMaxEntrySize with a malformed value from config.","commonSituations":"Reading limits from env vars or config files without parseInt/Number conversion ('100' string), arithmetic producing NaN (undefined * 1024), fractions from byte-size division (maxSize = bytes/1024 yielding 1.5), or negated defaults.","solutions":["Coerce and validate before constructing: max = Number.parseInt(raw, 10) with a Number.isInteger check.","Round computed sizes: Math.floor(bytes / 1024) so the value is an integer.","Guard against NaN/Infinity: use Number.isFinite(v) && Number.isInteger(v) && v >= 0.","Fix the config source producing the bad value (env var, JSON field, CLI flag)."],"exampleFix":"// before\nconst cache = new LRUCache({ max: Number(process.env.CACHE_MAX) }); // '100' or NaN → TypeError\n// after\nconst raw = Number(process.env.CACHE_MAX);\nconst max = Number.isInteger(raw) && raw >= 0 ? raw : 100;\nconst cache = new LRUCache({ max });","handlingStrategy":"validation","validationCode":"function toNonNegativeInt(raw: unknown): number {\n  const n = typeof raw === \"number\" ? raw : Number(raw);\n  if (!Number.isInteger(n) || n < 0) throw new TypeError(`limit must be a non-negative integer, got ${raw}`);\n  return n;\n}","typeGuard":"function isNonNegativeInt(v: unknown): v is number {\n  return typeof v === \"number\" && Number.isInteger(v) && v >= 0;\n}","tryCatchPattern":"try {\n  cache = new LRUCache(options);\n} catch (err) {\n  if (err instanceof TypeError && err.message.includes(\"must be a non-negative integer\")) {\n    logger.warn(\"Invalid LRU limit, falling back to defaults\", { option: err.message.split(\" \")[0] });\n    cache = new LRUCache({ max: 1000 });\n  } else throw err;\n}","preventionTips":["parseInt/Number all limits coming from env/config before constructing.","Use Math.floor on computed sizes to avoid fractions.","Check Number.isFinite to exclude NaN/Infinity.","Keep a typed options interface so string config values can't leak in."],"tags":["validation","lru-cache","type-error","configuration"],"backgroundTag":"invalid-constructor-option","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}