← Debugging Guides

阅读中文版本

Node.js EADDRINUSE — Port Already in Use

Diagnose and resolve EADDRINUSE when a Node.js server cannot bind a port because another process is listening or a previous instance did not exit cleanly.

beginnerdebuggingnodejsportsdev-server

Symptoms

  • The process exits with Error: listen EADDRINUSE: address already in use :::3000
  • A dev server restart fails immediately while the old process still serves requests
  • Requests reach an outdated build because the old server keeps the port

Possible causes

  • Another process is listening on the same port, including a previous Node.js instance
  • The previous process did not exit because shutdown was not handled or a child process survived
  • IPv4 and IPv6 bindings conflict, or the port is reserved by another service

Diagnosis steps

Find the process listening on the port

lsof -i :3000   # macOS/Linux; Windows: netstat -ano | findstr :3000

Interpretation

The output shows the PID and command holding the port. If it is an older node process, the restart did not terminate it.

Confirm it is your process before stopping anything

ps -p <pid> -o pid,ppid,etime,command

Interpretation

Match the command line and start time to your project. Never kill an unknown process on a shared machine without checking.

Check for multiple listeners or a dual-stack conflict

ss -ltnp 'sport = :3000'   # Linux

Interpretation

More than one listener on the same port, or a listener on a different address family, explains why changing only one binding does not help.

Root cause

The operating system allows only one listener per address and port combination. The new process asks to bind the same socket that an existing process still holds, so the kernel rejects the bind with EADDRINUSE.

Fix

Stop the process that holds the port if it is yours, then start the server again. If the old process is a zombie or a child, terminate its process group. For development, make the port configurable and fail with a clear message instead of silently choosing another port.

Risk note

Killing a process can drop in-flight requests or lose unsaved work. Identify the process first; prefer SIGTERM and a graceful shutdown over SIGKILL.

Verify the fix

Start the server and confirm a single listener owns the port. Curl a known endpoint and verify the response comes from the new build, then restart once more to confirm shutdown releases the port.

What EADDRINUSE means

EADDRINUSE is a bind failure, not an application error. The kernel refuses to give your process a socket that another process already owns. The most common cause in development is that the previous server never exited.

lsof -i :3000
# or on Windows
netstat -ano | findstr :3000

Confirm the PID belongs to your project, then stop it gracefully:

kill -TERM <pid>

On Windows, taskkill /PID <pid> sends a termination request; /F should be a last resort.

Prevent it next time

Make the port configurable and print the resolved port at startup. Handle SIGTERM and SIGINT so the server closes its listener. In watch mode, ensure the tool waits for the old process to exit before starting a new one. If the port is intentionally occupied, fail with a clear message rather than silently binding a different port, which can make you test the wrong instance.

Verify

  • A single process listens on the expected port.
  • The response contains the new build identifier or a fresh startup log line.
  • Restarting the dev server succeeds without manual cleanup.
  • No orphaned child processes remain after shutdown.

If the process holding the port is not yours, choose a different port or coordinate with the owner; do not kill it blindly.