nodejs/node · error · ClientClosedError

UND_ERR_CLOSED

UND_ERR_CLOSED

Error message

The client is closed

What it means

ClientClosedError (UND_ERR_CLOSED) thrown by dispatch() when the client has been closed via close() but not yet destroyed. close() stops accepting new dispatches while letting in-flight ones finish; any new dispatch during that window throws. As with destroyed, an onResponseError handler receives the error instead.

Source

Thrown at deps/undici/src/lib/dispatcher/dispatcher-base.js:169

    if (!handler || typeof handler !== 'object') {
      throw new InvalidArgumentError('handler must be an object')
    }

    try {
      if (!opts || typeof opts !== 'object') {
        throw new InvalidArgumentError('opts must be an object.')
      }

      if (opts.dispatcher) {
        throw new InvalidArgumentError('opts.dispatcher is not supported by instance methods. Pass opts.dispatcher to the top-level undici functions or call the dispatcher instance method directly.')
      }

      if (this[kDestroyed] || this[kOnDestroyed]) {
        throw new ClientDestroyedError()
      }

      if (this[kClosed]) {
        throw new ClientClosedError()
      }

      return this[kDispatch](opts, handler)
    } catch (err) {
      if (typeof handler.onResponseError !== 'function') {
        throw err
      }

      handler.onResponseError(null, err)

      return false
    }
  }
}

module.exports = DispatcherBase

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Do not dispatch after close(); create a new client for further work.
  2. Guard with `if (client.closed) { ... }` before dispatching.
  3. Use destroy() if you want immediate teardown, or wait for close to settle before re-creating.
  4. Implement onResponseError on the handler to absorb the error.

Example fix

// before
await client.close()
client.dispatch(opts, handler) // throws
// after
await client.close()
const client2 = new Client(url)
client2.dispatch(opts, handler)
Defensive patterns

Strategy: type-guard

Validate before calling

function ensureOpen(client) {
  if (client.closed || client.destroyed) {
    return new Client(client.url);
  }
  return client;
}

Type guard

!client.closed && !client.destroyed

Try / catch

try { client.dispatch(opts, handler); } catch (e) { if (e.code === 'UND_ERR_CLOSED') { const c = new Client(url); c.dispatch(opts, handler); } else throw e; }

Prevention

When it happens

Trigger: Calling `client.dispatch(...)` after `await client.close()`. Fires at dispatcher-base.js:168-169.

Common situations: Graceful shutdown that calls close() but a queued task then tries to dispatch; retry logic that does not check `client.closed`; mixing close() and destroy() semantics and dispatching in between.

Related errors


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