EADDRINUSE high
Node.js Error: listen EADDRINUSE: address already in use :::3000
Node can't start its server because another process is already listening on the target port.
What you see
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (node:net:1817:16)
at listenInCluster (node:net:1865:12)
Emitted 'error' event on Server instance at:
code: 'EADDRINUSE', errno: -48, syscall: 'listen', address: '::', port: 3000 What’s actually happening
The process throws on app.listen() / server.listen() and exits with code 1 unless you catch the error. You'll see this constantly in development after a crash or a Ctrl-C that didn't fully tear down — the old instance is still bound. With nodemon or a watcher, a fast file-save can spawn a new process before the old one releases the socket. The :::3000 form means an IPv6 wildcard bind; 0.0.0.0:3000 is the IPv4 equivalent.
Common causes
- A previous run of the same app is still alive and holding the port (orphaned after a crash or a detached background process).
- A watcher (nodemon, ts-node-dev, next dev) restarted before the prior child exited, so two instances race for the port.
- Another app genuinely uses that port — a second Node service, a Docker container publishing it, or a system daemon.
- The port is hardcoded and a teammate or another shell already launched the service.
- On macOS, a SIGINT left the listener in TIME_WAIT or the parent never reaped the child.
How to fix it
- Identify and kill the holdersudo lsof -i :3000 or ss -tlnp 'sport = :3000' gives the PID. Then kill <PID> (kill -9 only if it ignores the polite signal). On macOS the one-liner is: kill -9 $(lsof -ti :3000).
- Make the port configurableRead it from the environment: const port = process.env.PORT || 3000. Then PORT=3001 node server.js sidesteps the clash without editing code. Hardcoded ports are the root of most repeat offenses.
- Handle the error instead of crashingserver.on('error', err => { if (err.code === 'EADDRINUSE') { console.error(`Port ${port} busy`); process.exit(1); } }). At least you get a readable message instead of an unhandled 'error' event stack trace.
- Fix the restart race in your watcherIf nodemon double-spawns, add a small restart delay (nodemon --delay 1) or ensure your shutdown handler calls server.close() on SIGINT/SIGTERM so the socket frees before the next start.
- In Docker, check for a port-publish collisionTwo containers (or a container and the host) mapping -p 3000:3000 will collide on the host side. docker ps shows the published ports; remap one to -p 3001:3000.
Stop it recurring
Always read the port from process.env.PORT and add a SIGINT/SIGTERM handler that calls server.close() for a clean release.
Related errors