Fix Express.js EADDRINUSE "Port in Use" Error

Your Express server crashed with EADDRINUSE. Here is how to fix it immediately and prevent it from happening again.

Updated April 20265 min read

The error

When calling app.listen(), Express throws:

Error: listen EADDRINUSE: address already in use :::3000
    at Server.setupListenHandle [as _setupListenHandle] (node:net:1897:16)
    at listenInCluster (node:net:1945:12)
    at Server.listen (node:net:2037:7)

This means another process is already bound to the port your Express app is trying to use.

Instant fix: portrm

$ ptrm fix 3000
  Port 3000 in use
  node (PID 54321) - server.js
  Killed PID 54321. Port 3000 is free.

$ node server.js  # starts normally

Manual fix

# macOS / Linux
$ lsof -ti :3000 | xargs kill -9

# Windows
> netstat -ano | findstr :3000
> taskkill /PID <PID> /F

Handle EADDRINUSE in code

Instead of crashing, catch the error and show a helpful message:

const app = require('express')();
const PORT = process.env.PORT || 3000;

const server = app.listen(PORT, () => {
  console.log(`Listening on port ${PORT}`);
});

server.on('error', (err) => {
  if (err.code === 'EADDRINUSE') {
    console.error(`Port ${PORT} is in use.`);
    console.error('Run: ptrm fix ' + PORT);
    process.exit(1);
  }
  throw err;
});

Add graceful shutdown

The most common cause of EADDRINUSE in Express is an orphaned process from a previous run. Add graceful shutdown to prevent this:

function shutdown() {
  console.log('Shutting down gracefully...');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });

  // Force exit after 5s if connections hang
  setTimeout(() => process.exit(1), 5000);
}

process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);

Use a dynamic port fallback

Try the preferred port, then fall back to a random available port:

const net = require('net');

function getAvailablePort(preferred) {
  return new Promise((resolve) => {
    const srv = net.createServer();
    srv.listen(preferred, () => {
      srv.close(() => resolve(preferred));
    });
    srv.on('error', () => {
      srv.listen(0, () => {
        const port = srv.address().port;
        srv.close(() => resolve(port));
      });
    });
  });
}

getAvailablePort(3000).then((port) => {
  app.listen(port, () => {
    console.log(`Server running on port ${port}`);
  });
});

nodemon / ts-node restart issues

When using nodemon, file saves trigger restarts. If the old process does not exit cleanly, the new one gets EADDRINUSE. Fix:

// nodemon.json
{
  "signal": "SIGTERM",
  "delay": 500
}

This gives the old process time to close the socket before the new one starts.

Stop debugging port conflicts

portrm finds and kills orphaned Express processes in one command. Add ptrm fix 3000 to your npm scripts.