nodejs/node · error · InvalidArgumentError
UND_ERR_INVALID_ARG
UND_ERR_INVALID_ARG
Error message
factory must be a function.
What it means
Thrown by the Agent constructor. The factory option selects which Dispatcher (Client or Pool) the Agent creates per origin; the default returns a Pool (or a single-connection Client when connections === 1). Because the Agent invokes factory(origin, opts) as a function on every new origin, a non-function value cannot be called, so it is rejected at construction.
Source
Thrown at deps/undici/src/lib/dispatcher/agent.js:27
const kOnConnect = Symbol('onConnect')
const kOnDisconnect = Symbol('onDisconnect')
const kOnConnectionError = Symbol('onConnectionError')
const kOnDrain = Symbol('onDrain')
const kFactory = Symbol('factory')
const kOptions = Symbol('options')
const kOrigins = Symbol('origins')
function defaultFactory (origin, opts) {
return opts && opts.connections === 1
? new Client(origin, opts)
: new Pool(origin, opts)
}
class Agent extends DispatcherBase {
constructor ({ factory = defaultFactory, maxOrigins = Infinity, connect, ...options } = {}) {
if (typeof factory !== 'function') {
throw new InvalidArgumentError('factory must be a function.')
}
if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') {
throw new InvalidArgumentError('connect must be a function or an object')
}
if (typeof maxOrigins !== 'number' || Number.isNaN(maxOrigins) || maxOrigins <= 0) {
throw new InvalidArgumentError('maxOrigins must be a number greater than 0')
}
super(options)
if (connect && typeof connect !== 'function') {
connect = { ...connect }
}
this[kOptions] = { ...util.deepClone(options), maxOrigins, connect }
this[kFactory] = factoryView on GitHub (pinned to 1b2de5e052)
Solutions
- Provide factory as a function: new Agent({ factory: (origin, opts) => new Pool(origin, opts) }).
- If you want the default behavior, omit the factory option entirely.
- Check that any imported Dispatcher class is actually defined (not undefined) before referencing it.
Example fix
// before
const agent = new Agent({ factory: Pool })
// after
const agent = new Agent({ factory: (origin, opts) => new Pool(origin, opts) }) Defensive patterns
Strategy: type-guard
Validate before calling
function buildAgent(options = {}) {
if (options.factory !== undefined && typeof options.factory !== 'function') {
throw new TypeError('Agent options.factory must be a function')
}
return new Agent(options)
} Type guard
const isFactory = (f) => typeof f === 'function'
Prevention
- Wrap class references: factory: (origin, opts) => new Pool(origin, opts).
- Check imported symbols are defined before using them in factory.
When it happens
Trigger: new Agent({ factory: <not a function> }) — e.g. factory: Pool, factory: {}, factory: 'pool', or a destructured import that resolved to undefined.
Common situations: Passing a class reference without wrapping it (factory: Pool instead of (origin, opts) => new Pool(origin, opts)); a default import that returned undefined due to an ESM/CJS interop issue; a config object where factory was overwritten by a merge.
Related errors
- UND_ERR_INVALID_ARG
- UND_ERR_INVALID_ARG
- UND_ERR_INVALID_ARG
- SqliteCacheStore options must be an object
- UND_ERR_INVALID_ARG
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/f4aa8515eb46d880.
Report an issue: GitHub.