can1357/oh-my-pi · error · TypeError
sizeCalculation return invalid (expect positive integer)
Error message
sizeCalculation return invalid (expect positive integer)
What it means
When a size-bounded LRUCache computes an entry's size, the sizeCalculation callback must return a positive integer (0 and negatives are rejected, as are NaN/fractions). A non-conforming return throws this TypeError from #entrySize, typically while adding an entry (set) or reading the cache's total size. This keeps total-size accounting valid — a zero or negative entry size would corrupt eviction decisions.
Source
Thrown at packages/utils/src/lru.ts:172
const [key, entry] = entries[index]!;
if (!this.#isStale(entry)) yield key;
}
}
/** Iterates fresh values from most to least recently used. */
*values(): Generator<V, void, unknown> {
const entries = [...this.#entries.values()];
for (let index = entries.length - 1; index >= 0; index--) {
const entry = entries[index]!;
if (!this.#isStale(entry)) yield entry.value;
}
}
#entrySize(value: V, key: K): number {
if (this.#sizeCalculation === undefined) return 0;
const size = this.#sizeCalculation(value, key);
if (!Number.isInteger(size) || size <= 0)
throw new TypeError("sizeCalculation return invalid (expect positive integer)");
return size;
}
#isStale(entry: Entry<V>): boolean {
return this.#ttl !== 0 && performance.now() - entry.start > this.#ttl;
}
#remove(key: K, entry: Entry<V>, reason: DisposeReason): void {
this.#dispose?.(entry.value, key, reason);
this.#entries.delete(key);
this.#calculatedSize -= entry.size;
}
}
View on GitHub (pinned to 9690622007)
Solutions
- Clamp in the callback: return Math.max(1, Math.ceil(rawSize)).
- Use Math.round/floor on computed byte sizes to guarantee an integer.
- Handle empty values explicitly — treat them as size 1 so they still count against maxSize.
- Make the callback total: guard against undefined/foreign value shapes and return a fallback size instead of NaN.
Example fix
// before sizeCalculation: (v: string) => v.length // empty string → 0 → TypeError // after sizeCalculation: (v: string) => Math.max(1, Buffer.byteLength(v));
Defensive patterns
Strategy: validation
Validate before calling
function safeSizeCalculation(measure: (v: never, k: never) => number) {
return (v: unknown, k: unknown): number => {
const raw = measure(v as never, k as never);
return Number.isFinite(raw) ? Math.max(1, Math.ceil(raw)) : 1;
};
} Type guard
function isValidEntrySize(n: unknown): n is number {
return typeof n === "number" && Number.isInteger(n) && n > 0;
} Try / catch
try {
cache.set(key, value);
} catch (err) {
if (err instanceof TypeError && err.message.includes("sizeCalculation return invalid")) {
logger.warn("sizeCalculation returned invalid size", { key });
cacheWithSafeCalc.set(key, value); // cache built with clamped sizeCalculation
} else throw err;
} Prevention
- Wrap every sizeCalculation with Math.max(1, Math.ceil(raw)) — empty values still occupy space.
- Never assume v.length/v.size exists; coerce and validate inside the callback.
- Test the callback against empty strings, empty objects, and null-ish values.
- Keep arithmetic integer-only (byte counts, not bytes/1000 fractions).
When it happens
Trigger: sizeCalculation returning 0 for empty strings/empty buffers, returning v.length when length can be 0, returning a fractional byte estimate, returning NaN (e.g. v.size undefined), or a callback with wrong assumptions about the value type (undefined.length).
Common situations: Caching empty strings or empty objects whose natural size is 0, byte-count helpers using values that aren't measured (Blob.size on a detached blob), math like bytes/1000 producing fractions, migrating from a cache library that allowed 0-size entries.
Related errors
- ${name} must be a non-negative integer
- sizeCalculation is required when a size limit is set
- Invalid pattern: {err}
- Destination option ${key} must be a string
- Destination option ${key} must be a finite number
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/af4517fb3f5ae661.
Report an issue: GitHub.