You ask your AI coding agent to run the app, and you get this:
Error: listen EADDRINUSE: address already in use :::3000
Or worse, you don't get an error at all. The agent's server quietly starts on 3001, your browser is still on 3000, and you spend ten minutes wondering why your change isn't showing up.
I wrote a general guide to finding and stopping whatever is using port 3000 on a Mac. This post is about the version of the problem that AI agents cause, because it happens a lot more often now, and it's easy to prevent once you know why.
Why AI agents leave servers running
Claude Code, Cursor, Codex and similar tools all run shell commands for you. That's useful, but dev servers are a bad fit for how they work:
- Background servers. A dev server never finishes, so agents often start it in the background, check that the page loads, and move on. Nothing tells them to stop it later.
- Re-runs. Later in the same session, or in a new session, the agent wants to test again. It doesn't know a server is already running, so it starts another one. Many dev servers (Vite, for example) quietly move to the next free port, so you end up with copies on 3000, 3001 and 3002.
-
Crashed or closed sessions. If the agent's session crashes, or you close the window, the server it started can keep running with no parent. On macOS it gets adopted by
launchdand just sits there. - Several agents at once. Two agents on one project both want the same port. The second one either fails or picks a random port.
So the fix has two parts: clean up what's running now, then stop it happening again.
Step 1: find what's listening on the port
lsof -nP -iTCP:3000 -sTCP:LISTEN
-
-iTCP:3000looks only at TCP on port 3000. -
-sTCP:LISTENshows only the process that's listening, which is the one holding the port. Without it you'd also see your browser's connections. -
-nand-Pskip name lookups, so it's faster and shows3000instead of a service name.
You'll get something like:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 48213 tj 23u IPv6 0x... 0t0 TCP *:3000 (LISTEN)
To see every listening port at once, which is handy after a long agent session:
lsof -nP -iTCP -sTCP:LISTEN
Step 2: check what it is and who started it
Before you stop anything, find out what it is. Take the PID and ask ps:
ps -o pid,ppid,etime,command -p 48213
PID PPID ELAPSED COMMAND
48213 1 01:12:40 node /Users/you/code/shop/node_modules/.bin/vite
- command shows the full command line, which usually includes the project folder. That tells you which project it belongs to.
- etime (shown as ELAPSED) is how long it's been running. An hour means it's not the server you just started.
-
ppid is the parent process. Run the same
pscommand on that PID to see what launched it. You'll often find a shell that belongs to your editor or AI tool. If the PPID is1, the thing that started it has already gone, and this is a leftover.
If it's a database, Docker or something you didn't expect, stop and think before going further.
Step 3: stop it safely
Ask it to quit first:
kill 48213
This sends SIGTERM, a polite "please quit", so the server can close files and connections properly. Wait a couple of seconds and run the lsof command from step 1 again.
Only if it's still there:
kill -9 48213
kill -9 ends it straight away with no cleanup. Use it second, not first.
If you're sure the listener on 3000 is a leftover dev server and want one line, this stops only the process that's listening on that port:
kill $(lsof -t -iTCP:3000 -sTCP:LISTEN)
The -sTCP:LISTEN part matters. Without it, lsof also returns other processes connected to the port, such as your browser, and you'd send them the signal too.
If the server comes straight back after you stop it, something is restarting it, such as Docker, a process manager or a tool like Laravel Herd. Stop it there instead.
Step 4: stop it happening again
Cleaning up is fine once. Doing it every day gets old. These changes made the biggest difference for me.
Give each agent a fixed port. Put it in the project's .env:
PORT=3001
And make the dev server fail instead of drifting to a random port. For Vite:
npm run dev -- --port 3001 --strictPort
A server that fails loudly on a busy port is much easier to deal with than one that silently moves to 3004.
If you run more than one agent on the same project, give each one its own folder (a git worktree) and its own port, so they never compete for 3000.
Write the rules down for the agent. Agents read CLAUDE.md (Claude Code) or AGENTS.md (Codex and others). Add a short section:
## Dev server
- Run it with: `npm run dev -- --port 3001 --strictPort`
- Use port 3001 only.
- Before starting a server, check if one is already running:
`lsof -nP -iTCP:3001 -sTCP:LISTEN`. If it is, reuse it.
- Stop any server you started when the task is done.
This one change cut my duplicate servers by more than anything else.
Stop servers when you're done. At the end of a session, ask the agent to stop what it started, or run lsof -nP -iTCP -sTCP:LISTEN yourself and clear out anything left over. It takes ten seconds and saves the confusing "why is my old code showing?" moment the next morning.
Doing this in Lokstack
Disclosure: I build Lokstack, a Mac app, and this part is about my own app.
I got tired of running these commands after every agent session, so Lokstack does the looking for you. Search for a port and it shows what's using it, which project it belongs to and who started it. Stop asks the program to quit, like kill. Force stops it straight away, like kill -9. The Agents screen has a Left running panel that lists the dev servers AI tools started and didn't stop. All of that is on the free plan.
Here's a short video of finding and freeing a busy port:
If you'd rather stay in Terminal, the commands above are all you need. https://lokstack.app
What's the most servers you've found an agent left running? Tell me in the comments.


Top comments (0)