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
- Call `server.close()` and wait for the 'close' event before calling `listen()` again.
- Guard with `if (server.listening) return;` before listen.
- Create a new `net.Server` instance when the old one may still be bound.
- 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
- Check server.listening before every listen call.
- Wait for the 'close' event before rebinding; the close callback is the only safe re-listen point.
- In tests, create a fresh server per case or close in afterEach.
- In EADDRINUSE retries, add backoff and re-listen from close, not from the error handler.
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
- Unsupported transport: '${transport}'
- unknown override kind: ${override.kind}
- Destroy hook of "${id}" errored
- Already closed
- Request closed
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/6942dbfd212a1f24.
Report an issue: GitHub.