nodejs/node · error · InvalidArgumentError
UND_ERR_INVALID_ARG
UND_ERR_INVALID_ARG
Error message
Invalid lookup. Must be a function
What it means
Thrown by the DNS interceptor factory (`InvalidArgumentError`, code `UND_ERR_INVALID_ARG`) when `lookup` is provided and is not a function. `lookup` lets you plug in a custom resolver (e.g. `dns.lookup`, a multi-resolver strategy, or a service-discovery hook) instead of the default Node resolver. The interceptor calls it per hostname to obtain address records, so anything non-callable would fail at runtime inside the dispatch path.
Source
Thrown at deps/undici/src/lib/interceptor/dns.js:495
interceptorOpts?.affinity != null &&
interceptorOpts?.affinity !== 4 &&
interceptorOpts?.affinity !== 6
) {
throw new InvalidArgumentError('Invalid affinity. Must be either 4 or 6')
}
if (
interceptorOpts?.dualStack != null &&
typeof interceptorOpts?.dualStack !== 'boolean'
) {
throw new InvalidArgumentError('Invalid dualStack. Must be a boolean')
}
if (
interceptorOpts?.lookup != null &&
typeof interceptorOpts?.lookup !== 'function'
) {
throw new InvalidArgumentError('Invalid lookup. Must be a function')
}
if (
interceptorOpts?.pick != null &&
typeof interceptorOpts?.pick !== 'function'
) {
throw new InvalidArgumentError('Invalid pick. Must be a function')
}
if (
interceptorOpts?.storage != null &&
(typeof interceptorOpts?.storage?.get !== 'function' ||
typeof interceptorOpts?.storage?.set !== 'function' ||
typeof interceptorOpts?.storage?.full !== 'function' ||
typeof interceptorOpts?.storage?.delete !== 'function'
)
) {
throw new InvalidArgumentError('Invalid storage. Must be a object with methods: { get, set, full, delete }')View on GitHub (pinned to 1b2de5e052)
Solutions
- Pass a function reference, e.g. `lookup: dns.lookup` after `const dns = require('node:dns')`, or `lookup: myCustomResolver`.
- Make sure you import the function, not the module: pass `dns.lookup`, not `dns`.
- If using a service-discovery client, pass its resolve callback directly, not a config object wrapping it.
Example fix
// before
const dns = require('node:dns')
interceptor dns({ lookup: 'dns.lookup' })
dns({ lookup: { resolve: customResolve } })
// after
dns({ lookup: dns.lookup })
dns({ lookup: customResolve }) Defensive patterns
Strategy: type-guard
Validate before calling
function validateLookup(v) {
if (v == null) return undefined
if (typeof v !== 'function') throw new Error('lookup must be a function')
return v
} Type guard
function isFunction(v) {
return typeof v === 'function'
} Prevention
- Pass the resolver function reference, not a string or module.
- Import node:dns and pass dns.lookup directly.
When it happens
Trigger: Passing `lookup: 'dns.lookup'` (a string name), an object `{ resolve: fn }`, a hostname string, or a value that is not callable. The guard is `lookup != null && typeof !== 'function'`.
Common situations: Referencing `dns.lookup` without importing `node:dns`; passing a bound method's name as a string; wrapping the resolver in an object (e.g. `{ lookup: realLookup }`) and passing the wrapper instead of the function. Default behavior uses Node's built-in resolver, so omit unless you need customization.
Related errors
- Invalid maxTTL. Must be a positive number
- Invalid maxItems. Must be a positive number and greater than
- Invalid affinity. Must be either 4 or 6
- Invalid dualStack. Must be a boolean
- expected opts.methods to be an array, got ${typeof methods}
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/efe484bd26e22e50.
Report an issue: GitHub.