{"record":{"id":"c2cbba44a18a16bd","repo":"can1357/oh-my-pi","slug":"sizecalculation-is-required-when-a-size-limit-is-s","errorCode":null,"errorMessage":"sizeCalculation is required when a size limit is set","messagePattern":"sizeCalculation is required when a size limit is set","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/lru.ts","lineNumber":59,"sourceCode":"\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\");\n\t\tthis.#maxSize = positiveInteger(options.maxSize, \"maxSize\");\n\t\tconst explicitMaxEntrySize = positiveInteger(options.maxEntrySize, \"maxEntrySize\");\n\t\tthis.#maxEntrySize = explicitMaxEntrySize || this.#maxSize;\n\t\tthis.#ttl = positiveInteger(options.ttl, \"ttl\");\n\t\tif (this.#max === 0 && this.#maxSize === 0 && this.#ttl === 0) {\n\t\t\tthrow new TypeError(\"At least one of max, maxSize, or ttl is required\");\n\t\t}\n\t\tif ((this.#maxSize !== 0 || this.#maxEntrySize !== 0) && options.sizeCalculation === undefined) {\n\t\t\tthrow new TypeError(\"sizeCalculation is required when a size limit is set\");\n\t\t}\n\t\tthis.#sizeCalculation = options.sizeCalculation;\n\t\tthis.#updateAgeOnGet = options.updateAgeOnGet === true;\n\t\tthis.#dispose = options.dispose;\n\t}\n\n\t/** Number of entries, including stale entries not yet removed by `get`. */\n\tget size(): number {\n\t\treturn this.#entries.size;\n\t}\n\n\t/** Aggregate calculated size of retained entries. */\n\tget calculatedSize(): number {\n\t\treturn this.#calculatedSize;\n\t}\n\n\t/** Stores a value and makes it most recently used. */\n\tset(key: K, value: V | undefined): this {","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/lru.ts#L41-L77","documentation":"When an LRUCache is given a size limit (maxSize or maxEntrySize > 0), it needs a sizeCalculation callback to determine each entry's size — the library cannot infer it. Constructing a size-bounded cache without that function throws this TypeError at construction time rather than failing later on the first set().","triggerScenarios":"new LRUCache({ maxSize: 1024 }) with no sizeCalculation, new LRUCache({ maxEntrySize: 100 }) alone, or config-merge dropping the sizeCalculation key while keeping maxSize.","commonSituations":"Spreading options objects where functions were serialized/dropped (JSON round-trip of options), copy-pasted cache setup that kept maxSize but removed the callback, TypeScript type loosening via as any that bypassed the option type check.","solutions":["Provide sizeCalculation: (value, key) => byteLength or similar when setting maxSize/maxEntrySize.","If you don't need size bounding, remove maxSize/maxEntrySize and bound with max or ttl instead.","For string/buffer caches a typical calculation is v => Buffer.byteLength(String(v)).","Keep options objects in memory — don't serialize cache options through JSON."],"exampleFix":"// before\nconst cache = new LRUCache({ maxSize: 1024 * 1024 }); // TypeError\n// after\nconst cache = new LRUCache({\n  maxSize: 1024 * 1024,\n  sizeCalculation: (v: string) => Buffer.byteLength(v),\n});","handlingStrategy":"validation","validationCode":"function assertSizeConfig(o: { maxSize?: number; maxEntrySize?: number; sizeCalculation?: (v: unknown, k: unknown) => number }): void {\n  if ((o.maxSize || o.maxEntrySize) && typeof o.sizeCalculation !== \"function\") {\n    throw new TypeError(\"sizeCalculation function is required with maxSize/maxEntrySize\");\n  }\n}","typeGuard":"function hasSizeCalculation(o: { maxSize?: number; maxEntrySize?: number; sizeCalculation?: unknown }): o is { sizeCalculation: (v: never, k: never) => number } & Record<string, unknown> {\n  return Boolean(o.maxSize || o.maxEntrySize) && typeof o.sizeCalculation === \"function\";\n}","tryCatchPattern":"try {\n  cache = new LRUCache(options);\n} catch (err) {\n  if (err instanceof TypeError && err.message.includes(\"sizeCalculation is required\")) {\n    logger.warn(\"Size-bounded LRU missing sizeCalculation; adding byte-length estimator\");\n    cache = new LRUCache({ ...options, sizeCalculation: (v: unknown) => Math.max(1, Buffer.byteLength(String(v))) });\n  } else throw err;\n}","preventionTips":["Keep cache options in memory — JSON round-trips drop functions like sizeCalculation.","Pair every maxSize/maxEntrySize with its sizeCalculation in the same object literal.","Add a constructor-options test that builds the cache so the error fires in CI, not production."],"tags":["lru-cache","validation","configuration","callback"],"backgroundTag":"missing-required-option","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}