nodejs/node · error · InvalidArgumentError
Invalid maxTTL. Must be a positive number
Error message
Invalid maxTTL. Must be a positive number
What it means
Thrown by the DNS interceptor's factory (an `InvalidArgumentError`, code `UND_ERR_INVALID_ARG`) when `maxTTL` is provided and is either not a number or is less than 0. The DNS interceptor caches resolved address records client-side to cut lookup latency; `maxTTL` (expressed in milliseconds, default 10000) caps how long a cached entry lives, overriding the record's own TTL when shorter. Note the code allows `0` despite the message saying 'positive' — only negatives and non-numbers are rejected.
Source
Thrown at deps/undici/src/lib/interceptor/dns.js:463
break
}
case 'ENOTFOUND':
this.#state.deleteRecords(this.#origin)
super.onResponseError(controller, err)
break
default:
super.onResponseError(controller, err)
break
}
}
}
module.exports = interceptorOpts => {
if (
interceptorOpts?.maxTTL != null &&
(typeof interceptorOpts?.maxTTL !== 'number' || interceptorOpts?.maxTTL < 0)
) {
throw new InvalidArgumentError('Invalid maxTTL. Must be a positive number')
}
if (
interceptorOpts?.maxItems != null &&
(typeof interceptorOpts?.maxItems !== 'number' ||
interceptorOpts?.maxItems < 1)
) {
throw new InvalidArgumentError(
'Invalid maxItems. Must be a positive number and greater than zero'
)
}
if (
interceptorOpts?.affinity != null &&
interceptorOpts?.affinity !== 4 &&
interceptorOpts?.affinity !== 6
) {
throw new InvalidArgumentError('Invalid affinity. Must be either 4 or 6')View on GitHub (pinned to 1b2de5e052)
Solutions
- Pass `maxTTL` as a positive number in milliseconds, e.g. `maxTTL: 60_000` for one minute.
- If your config uses seconds, convert: `maxTTL: ttlSeconds * 1000`.
- Coerce env strings with `Number(...)` and validate finiteness before passing.
- To disable caching expiry, omit `maxTTL` (default 10000 ms) rather than passing a sentinel.
Example fix
// before
interceptor dns({ maxTTL: '60000' }) // string
dns({ maxTTL: 60 }) // seconds, far too short
dns({ maxTTL: process.env.DNS_TTL })
// after
dns({ maxTTL: 60000 }) // ms = 60s
dns({ maxTTL: Number(process.env.DNS_TTL) }) Defensive patterns
Strategy: validation
Validate before calling
function validateMaxTTL(v) {
if (v == null) return undefined
const n = Number(v)
if (typeof n !== 'number' || n < 0) {
throw new Error('maxTTL must be a non-negative number (milliseconds)')
}
return n // remember: milliseconds, not seconds
} Type guard
function isNonNegativeNumber(v) {
return typeof v === 'number' && v >= 0
} Prevention
- Treat DNS maxTTL as milliseconds (default 10000).
- Coerce env-provided TTLs with Number().
- Convert seconds→ms at the config boundary.
When it happens
Trigger: Passing `maxTTL` as a string (`'10000'`), a negative number, `NaN`, or an object. The guard checks `interceptorOpts?.maxTTL != null && (typeof !== 'number' || < 0)`, so `0` passes. Value is interpreted in milliseconds, not seconds.
Common situations: Configuring TTL in seconds while the library expects milliseconds (off by 1000×); reading TTL from env as a string; copying a value from a docs example that used seconds. The seconds-vs-milliseconds confusion is the dominant real-world cause.
Related errors
- Invalid maxItems. Must be a positive number and greater than
- Invalid affinity. Must be either 4 or 6
- Invalid dualStack. Must be a boolean
- UND_ERR_INVALID_ARG
- expected ${name} to be an array or undefined, got ${typeof o
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/b5c5035c534b2cb3.
Report an issue: GitHub.