can1357/oh-my-pi · error · TypeError
At least one of max, maxSize, or ttl is required
Error message
At least one of max, maxSize, or ttl is required
What it means
An LRUCache must be bounded in some way: by entry count (max), by total size (maxSize), or by lifetime (ttl). Constructing one with none of these (all absent or 0) throws this TypeError, because an unbounded LRU would silently grow forever — the library refuses to create a cache that provides no eviction policy.
Source
Thrown at packages/utils/src/lru.ts:56
readonly #entries = new Map<K, Entry<V>>();
readonly #max: number;
readonly #maxSize: number;
readonly #maxEntrySize: number;
readonly #sizeCalculation: ((value: V, key: K) => number) | undefined;
readonly #ttl: number;
readonly #updateAgeOnGet: boolean;
readonly #dispose: ((value: V, key: K, reason: DisposeReason) => void) | undefined;
#calculatedSize = 0;
/** Creates an empty cache. */
constructor(options: LRUCacheOptions<K, V>) {
this.#max = positiveInteger(options.max, "max");
this.#maxSize = positiveInteger(options.maxSize, "maxSize");
const explicitMaxEntrySize = positiveInteger(options.maxEntrySize, "maxEntrySize");
this.#maxEntrySize = explicitMaxEntrySize || this.#maxSize;
this.#ttl = positiveInteger(options.ttl, "ttl");
if (this.#max === 0 && this.#maxSize === 0 && this.#ttl === 0) {
throw new TypeError("At least one of max, maxSize, or ttl is required");
}
if ((this.#maxSize !== 0 || this.#maxEntrySize !== 0) && options.sizeCalculation === undefined) {
throw new TypeError("sizeCalculation is required when a size limit is set");
}
this.#sizeCalculation = options.sizeCalculation;
this.#updateAgeOnGet = options.updateAgeOnGet === true;
this.#dispose = options.dispose;
}
/** Number of entries, including stale entries not yet removed by `get`. */
get size(): number {
return this.#entries.size;
}
/** Aggregate calculated size of retained entries. */
get calculatedSize(): number {
return this.#calculatedSize;
}View on GitHub (pinned to 9690622007)
Solutions
- Pass at least one bound: new LRUCache({ max: 100 }) is the simplest fix.
- For size-based bounding, set maxSize plus a sizeCalculation function.
- For time-based expiry, set ttl (milliseconds), optionally with updateAgeOnGet.
- Validate merged config before construction and apply a sane default limit when none is configured.
Example fix
// before
const cache = new LRUCache({}); // TypeError: At least one of max, maxSize, or ttl is required
// after
const cache = new LRUCache({ max: 1000 }); Defensive patterns
Strategy: validation
Validate before calling
function assertBounded(o: { max?: number; maxSize?: number; ttl?: number }): void {
if (!o.max && !o.maxSize && !o.ttl) throw new TypeError("LRU options need at least one of max, maxSize, ttl");
} Type guard
function isBounded(o: { max?: number; maxSize?: number; ttl?: number }): boolean {
return Boolean(o.max || o.maxSize || o.ttl);
} Try / catch
try {
cache = new LRUCache(options);
} catch (err) {
if (err instanceof TypeError && err.message.includes("At least one of max, maxSize, or ttl")) {
logger.warn("Unbounded LRU options rejected; applying default bound");
cache = new LRUCache({ ...options, max: 1000 });
} else throw err;
} Prevention
- Always set an explicit max as a safety default in config-driven setups.
- After merging/parsing config, assert at least one bound exists before constructing.
- Never build options objects by deleting limit keys; construct from validated inputs.
When it happens
Trigger: new LRUCache({}) or new LRUCache({ maxEntrySize: 10, sizeCalculation }) with no max/maxSize/ttl, or building options from config where all limit keys are missing/0 (e.g. max: parseInt(undefined) → NaN path already caught, but explicit { max: 0, maxSize: 0, ttl: 0 }).
Common situations: Config-driven construction where the user left all limits blank, refactored code that moved limits into a defaults object that was never merged, or tests copying a options object and deleting the limit keys.
Understand the failure class
Background: "Must pass :limit option" / "Missing required option" — required option errors explained — this error's family across 41 libraries.
Related errors
- ${name} must be a non-negative integer
- sizeCalculation is required when a size limit is set
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains an empty provider
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains a provider id wit
- ${name} path does not exist: ${trimmed}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/355407fed4d67228.
Report an issue: GitHub.