EADDRINUSE: definition and meaning
EADDRINUSE is the error code for “address already in use”. A program gets it when it tries to bind a socket to an address and port that another socket already holds, which usually means a server is already listening on that port.
EADDRINUSE means “address already in use”. It is raised when a program asks the operating system to bind a socket to an address and port that another socket already holds. For a server, that usually means something is already listening on the port you want.
Where you see it
POSIX lists EADDRINUSE among the errors that bind() can return, with the meaning “the specified address is already in use”. So the name is not specific to Node. Node.js lists it among its common system errors: an attempt to bind a server (net, http, or https) to a local address failed because another server on the local system already occupies that address.
If you call listen(3000) without naming a host, Node listens on the unspecified IPv6 address :: when IPv6 is available, or on 0.0.0.0 otherwise.
Handling it in code
Node’s documentation shows the failure arriving through the server’s error event, so handle it there. Check err.code, not the message text:
import net from "node:net";
const PORT = 3000;
const server = net.createServer();
server.once("error", (err) => {
if (err.code === "EADDRINUSE") {
console.error(`Port ${PORT} is taken`);
process.exit(1);
}
throw err;
});
server.listen(PORT);To avoid a clash entirely, listen on port 0 and the operating system assigns an arbitrary unused port. Read it from server.address().port after the listening event has fired.
Finding the owner
lsof -nP -iTCP:3000 -sTCP:LISTENThis lists the processes listening on TCP port 3000. The -n and -P flags skip host and port name lookups, -iTCP:3000 selects TCP port 3000, and -sTCP:LISTEN keeps only sockets in the listening state. The owner may be an older copy of your own dev server that never exited, or a different program entirely.
A common confusion
EADDRINUSE is the mirror image of ECONNREFUSED. EADDRINUSE happens on the server side, when something already holds the address you want to bind. Node describes ECONNREFUSED as a connection that the target actively refused, which usually means nothing is running at the address you tried to reach. Free or change the port to fix the first, and start the missing server to fix the second.
In Rig
- Who owns the port:
who_owns_portis a read-only tool. It tries to bind the port on127.0.0.1and::. If either bind fails, it looks up the listener withlsof -nP -iTCP:<port> -sTCP:LISTEN(netstat and tasklist on Windows) and returns each owner’s PID, command, and name (plus its working directory on macOS and Linux), along with the first free port in the 50 above the one you asked about. - Start conflicts: when the port is taken,
start_projectreturns aportConflictthat names the owner and a suggested port. Passresolve_port=killto stop the owner, orresolve_port=suggestto move the project to a free port. - Freeing a port:
kill_portsends SIGTERM, waits 400 ms, sends SIGKILL if the port is still bound, and checks again. It needsconfirm: trueand an Approve click in Rig. A project-scoped token can only free that project’s own port. - After a crash: when Rig restarts a project automatically and the failure was EADDRINUSE, it frees the port first.
Related terms
Sources
- Node.js errors: common system errors (v22.12.0 documentation)
- Node.js net: server.listen, the error event, and port 0
- bind(3p): POSIX Programmer’s Manual
- bind(2): Linux manual page
- lsof documentation: options
- Rig documentation: Common port conflicts (who_owns_port, kill_port, and start conflicts confirmed in Rig’s source code)
- Rig documentation: Ports and crashes (auto-restart port freeing confirmed in Rig’s source code)
See how Rig fits in
Rig is a Mac app that starts, stops, and watches the processes in a project from one window. The knowledge base explains each part in plain language.