remix-run/remix · error · RangeError
${limit} must be a non-negative safe integer
Error message
${limit} must be a non-negative safe integer What it means
The route-pattern matcher lets you cap resource usage via a limits object (max pattern size, nesting, work steps, etc.). resolveMatcherLimits merges defaults with user-supplied limits and requires every value to be a non-negative safe integer; anything else — negative, fractional, NaN, Infinity, or beyond Number.MAX_SAFE_INTEGER — throws a RangeError naming the offending limit key.
Source
Thrown at packages/route-pattern/src/lib/match/limits.ts:68
actual: number
}
export function createMatchWorkBudget(maximum: number): MatchWorkBudget {
return { maximum, actual: 0 }
}
export function consumeMatchWork(budget: MatchWorkBudget, count: number): void {
let actual = budget.actual + count
if (!Number.isSafeInteger(actual)) actual = Number.MAX_SAFE_INTEGER
checkMatcherLimit('maxMatchWork', budget.maximum, actual)
budget.actual = actual
}
export function resolveMatcherLimits(limits?: Partial<MatcherLimits>): MatcherLimits {
let result = { ...defaultMatcherLimits, ...limits }
for (let [limit, maximum] of Object.entries(result)) {
if (!Number.isSafeInteger(maximum) || maximum < 0) {
throw new RangeError(`${limit} must be a non-negative safe integer`)
}
}
return result
}
export function checkMatcherLimit(
limit: keyof MatcherLimits,
maximum: number,
actual: number,
): void {
if (actual > maximum) throw new MatcherResourceError({ limit, maximum, actual })
}
View on GitHub (pinned to 9696913134)
Solutions
- Coerce config-sourced values with Number() and validate them before constructing the matcher
- Ensure each limit is a whole number >= 0 (omit keys you don't want to change)
- Default unset values rather than passing -1 or Infinity as 'unlimited' — just leave the key out
Example fix
// before
let matcher = new RoutePatternMatcher({ limits: { maxPatternLength: Number.MAX_VALUE } })
// after
let matcher = new RoutePatternMatcher({ limits: { maxPatternLength: 10_000 } }) Defensive patterns
Strategy: validation
Validate before calling
for (let [k, v] of Object.entries(limits)) {
if (!Number.isSafeInteger(v) || v < 0) throw new Error(`Bad limit ${k}=${v}`)
} Type guard
function isNonNegativeSafeInteger(value: unknown): value is number {
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0
} Prevention
- Convert env/config strings with Number() and validate
- Omit limits you don't need instead of passing sentinels
When it happens
Trigger: Constructing a matcher with a limits option like { maxPatternLength: -1 }, { nestingDepth: 1.5 }, { matchWork: Infinity }, or { paramCount: NaN }. Any single bad entry fails construction immediately.
Common situations: Loading limits from env vars or config files as strings ('100' fails Number.isSafeInteger? no — strings are not numbers, so string configs throw); computing limits dynamically and passing 0/negative sentinels; typo'd keys are ignored but bad values are not.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- matcher resource limit exceeded
- Expected at most ${maxTransforms} request transforms
- hmr must create a channel with a close function
- hmr must create a channel with an onFileEvents function
- hmr must create a channel with an updateWatchedFiles functio
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/158f7c0f88e1579a.
Report an issue: GitHub.