nodejs/node · error · InvalidArgumentError
UND_ERR_INVALID_ARG
UND_ERR_INVALID_ARG
Error message
invalid opts
What it means
Thrown by undici's StreamHandler constructor (the stream() API) when the first argument opts is falsy or not an object. stream() requires an options object describing the request (method, path, headers, factory, etc.) — passing null, undefined, a string URL, or a primitive is rejected.
Source
Thrown at deps/undici/src/lib/api/api-stream.js:62
const onError = (err) => finish(err, true)
const onFinish = () => finish()
stream.on('close', onClose)
stream.on('error', onError)
stream.on('finish', onFinish)
if (stream.closed) {
process.nextTick(onClose)
} else if (stream.writableFinished) {
process.nextTick(onFinish)
}
}
class StreamHandler extends AsyncResource {
constructor (opts, factory, callback) {
if (!opts || typeof opts !== 'object') {
throw new InvalidArgumentError('invalid opts')
}
const { signal, method, opaque, body, onInfo, responseHeaders } = opts
try {
if (typeof callback !== 'function') {
throw new InvalidArgumentError('invalid callback')
}
if (typeof factory !== 'function') {
throw new InvalidArgumentError('invalid factory')
}
if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {
throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')
}
if (method === 'CONNECT') {View on GitHub (pinned to 1b2de5e052)
Solutions
- Pass a plain options object as the first argument: stream({ method, path, headers, origin }, factory, callback).
- If you have a URL, merge it in: stream({ ...new URL(u), ...opts }, factory, callback).
- Default the bag to at least {} only if you intentionally want defaults, but prefer constructing it explicitly.
Example fix
// before
undici.stream(targetUrl, factory, cb) // URL string is not an object
// after
undici.stream({ ...new URL(targetUrl), method: 'GET' }, factory, cb) Defensive patterns
Strategy: validation
Validate before calling
function streamOpts(urlOrOpts, extra = {}) {
const base = typeof urlOrOpts === 'string' || urlOrOpts instanceof URL
? { ...new URL(urlOrOpts) }
: urlOrOpts
if (!base || typeof base !== 'object') throw new TypeError('stream opts must be an object')
return { ...base, ...extra }
}
// stream(streamOpts(url), factory, cb) Type guard
function isStreamOpts(v) { return !!v && typeof v === 'object' && !Array.isArray(v) } Prevention
- Remember stream()'s signature is (optsObject, factory, callback) — no string-URL overload.
- Build the opts bag explicitly; avoid passing possibly-undefined conditionals.
- Wrap with a helper that normalizes URL vs object input.
When it happens
Trigger: Calling undici.stream(url, factory, callback) positional style when the API expects stream({...opts}, factory, callback); passing only a URL string; passing undefined because the opts bag was conditionally built and came back empty.
Common situations: Confusing stream()'s signature with request()'s (which accepts a URL string as first arg); destructuring options into undefined; refactoring that split URL and options incorrectly.
Related errors
- UND_ERR_INVALID_ARG
- UND_ERR_INVALID_ARG
- UND_ERR_INVALID_ARG
- UND_ERR_INVALID_RETURN_VALUE
- UND_ERR_INVALID_ARG
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/e003bead39ef0328.
Report an issue: GitHub.