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

  1. Pass a plain options object as the first argument: stream({ method, path, headers, origin }, factory, callback).
  2. If you have a URL, merge it in: stream({ ...new URL(u), ...opts }, factory, callback).
  3. 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

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


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/e003bead39ef0328. Report an issue: GitHub.