# X_CACHE_LIMIT_INVALID

## Means

a tier is given a numeric limit outside the domain its option accepts, refused at **construction**. Three domains, and the refusal names which: a **capacity** (`maxEntries`, `maxBytes`) is a whole number greater than zero; a **duration** (`defaultTtlMs`, `loadDeadlineMs`, a driver's request budget) is a whole number of milliseconds greater than zero; a **similarity floor** is a number from 0 to 1. So finite zero, a negative, a fraction and an unsafe integer are all refused too — not only `NaN` and `Infinity`

## Typical cause

`maxEntries`, `maxBytes`, `defaultTtlMs`, `loadDeadlineMs` or a similarity floor arriving as `NaN` or `Infinity` — most often `Number(process.env.CACHE_LIMIT)` on an unset variable, which is `NaN` and is **not** nullish, so `??` does not catch it and `Math.max`/`Math.min` propagate it. The failure is the dangerous direction: a comparison against `NaN` is false for **every** entry, so the limit stops being enforced rather than being enforced wrongly — an unbounded cache that reports healthy. The cause names that mechanism **only for `NaN`**: the same refusal also catches `Infinity`, `0`, a fraction and an unsafe integer, where the bound is applied as given rather than disabled — a ceiling of `Infinity` is never exceeded and a similarity floor of `Infinity` is never met, which are opposite outcomes one sentence cannot claim at once. Raised at construction rather than on the first write, where `X_CACHE_TTL_INVALID` would have named the write instead of the config

## Fix

the cause names the tier and the option; set it in `app.config.ts`, and parse an environment value before passing it. Where the value is a call argument rather than a config key — `cacheStack({ loadDeadlineMs })`, `lookup(embedding, override)` — the fix names the **argument**, because sending that caller to `app.config.ts` names a key that does not exist. `bun run finite-bounds` is the repo-wide rule

[Cache](/error-codes#cache)
