denoland/deno · error · Error

ERR_SERVER_ALREADY_LISTEN

ERR_SERVER_ALREADY_LISTEN

Error message

Listen method has been called more than once without closing.

What it means

`server.listen()` throws ERR_SERVER_ALREADY_LISTEN when the server already holds a handle from a previous listen that was never closed. The check is `this._handle`, so even a listen that has not finished binding counts as active. One listen per server instance is allowed.

Source

Thrown at ext/node/polyfills/net.ts:2915

 * details).
 *
 * The `server.listen()` method can be called again if and only if there was an
 * error during the first `server.listen()` call or `server.close()` has been
 * called. Otherwise, an `ERR_SERVER_ALREADY_LISTEN` error will be thrown.
 *
 * One of the most common errors raised when listening is `EADDRINUSE`.
 * This happens when another server is already listening on the requested`port`/`path`/`handle`. One way to handle this would be to retry
 * after a certain amount of time:
 */
Server.prototype.listen = function (...args: unknown[]) {
  const normalized = _normalizeArgs(args);
  let options = normalized[0] as Partial<ListenOptions>;
  const cb = normalized[1];

  this._listeningId++;

  if (this._handle) {
    throw new ERR_SERVER_ALREADY_LISTEN();
  }

  if (cb !== null) {
    this.once("listening", cb);
  }

  // The 'net.server.listen' tracingChannel publishes the user-visible
  // listen() options. Doing it here (after _normalizeArgs but before any
  // pre-existing-handle short-circuits return) lets subscribers see the same
  // options the caller passed in (including ad-hoc fields like
  // `customOption` used by test-diagnostics-channel-net to verify the
  // payload is the original options object).
  if (netServerListenChannel.hasSubscribers) {
    netServerListenChannel.asyncStart.publish({ server: this, options });
  }

  const backlogFromArgs: number =
    // (handle, backlog) or (path, backlog) or (port, backlog)

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Call `server.close()` and wait for the 'close' event before calling `listen()` again.
  2. Guard with `if (server.listening) return;` before listen.
  3. Create a new `net.Server` instance when the old one may still be bound.
  4. In retry loops, schedule the retry from the close callback, not from the error handler.

Example fix

// before
server.listen(port);
server.listen(port + 1); // throws ERR_SERVER_ALREADY_LISTEN

// after
server.listen(port);
server.close(() => server.listen(port + 1));
Defensive patterns

Strategy: validation

Validate before calling

async function safeListen(server, ...args) {
  if (server.listening) {
    await new Promise((resolve) => server.close(resolve));
  }
  return server.listen(...args);
}

Try / catch

try {
  server.listen(port);
} catch (err) {
  if (err?.code === 'ERR_SERVER_ALREADY_LISTEN') {
    server.close(() => server.listen(port)); // re-listen from the close callback
  } else throw err;
}

Prevention

When it happens

Trigger: Calling `listen()` twice on the same server. A retry handler that calls `listen()` again from inside the first attempt's error or restart path. Hot-reload or test setups that reuse a module-level server without closing it.

Common situations: EADDRINUSE retry logic that re-calls listen immediately. Rebinding a server to another port at runtime without teardown. Tests that share one server across cases and call listen in each.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/6942dbfd212a1f24. Report an issue: GitHub.