nodejs/node · error · ClientDestroyedError

UND_ERR_DESTROYED

UND_ERR_DESTROYED

Error message

The client is destroyed

What it means

ClientDestroyedError (UND_ERR_DESTROYED) thrown by dispatch() when the client/dispatcher has been destroyed (or destruction is in progress, signalled by kOnDestroyed being populated). Once destroyed, the instance rejects all new dispatches. If the handler implements onResponseError, the error is delivered there instead of thrown.

Source

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

      .then(() => queueMicrotask(onDestroyed))
  }

  dispatch (opts, handler) {
    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
    }
  }
}

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Create a new Client/Pool after destroying the previous one.
  2. Guard dispatch with `if (client.destroyed) recreate();`.
  3. Sequence shutdown so in-flight dispatches complete before destroy() (use drain or await pending ops).
  4. Add an onResponseError handler to receive the error gracefully instead of throwing.

Example fix

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

Strategy: type-guard

Validate before calling

function dispatchOrRecreate(client, opts, handler) {
  if (client.destroyed) {
    client = new Client(client[kUrl]);
  }
  return client.dispatch(opts, handler);
}

Type guard

!client.destroyed

Try / catch

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

Prevention

When it happens

Trigger: Calling `client.dispatch(...)` after `await client.destroy()` (or while destroy callbacks are still queued). Fires at dispatcher-base.js:164-165.

Common situations: Reusing a pooled client after a connection-error-triggered destroy; shutdown race where one path destroys while another dispatches; destroy-on-idle timeout firing mid-request; forgetting to create a fresh Client after teardown.

Related errors


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