Behind the Scenes

I Asked AI to Stop a Dev Server. It Killed My Entire Browser, Twice

By the POMODIARY Team · · 3 min read

On September 14, every tab in my Chrome crashed at the same moment.

The Claude desktop app, ChatGPT and Obsidian went down too, all turning blank in the same second.

I stared at the screen for a few seconds before it clicked: I had seen this before. One week earlier, on September 7, exactly the same thing happened.

This post was first written in Chinese for the developer's WeChat Official Account (a blog-style publishing channel inside the WeChat messaging app) and adapted for English readers.

The culprit was a command that looked careful

Both times started the same way. I asked an AI coding agent to stop my local Next.js dev server, and it ran this line:

pkill -f "next-server" -U "$(id -u)"

Read on its own, the command even looks thoughtful: -f matches next-server against the full command line, and -U narrows it to "only processes owned by the current user". Belt and braces.

That second safety measure was exactly the problem.

macOS pkill does not reorder your arguments

On Linux, pkill uses GNU getopt. You can write the arguments in any order and it sorts them out: options go with options, patterns go with patterns.

On macOS, pkill uses BSD getopt: it stops parsing options at the first argument that is not an option, and everything after that is treated as a pattern. pkill also accepts several patterns at once, and a process matching any one of them counts.

So in the eyes of macOS, that command contains three patterns:

  • next-server
  • -U
  • 501 (my user ID)

And because -f matches against the full command line, what the command really meant was: kill every process whose command line contains -U or 501.

Apps built on Chromium or Electron, such as Chrome, Claude, ChatGPT and Obsidian, run a pile of Helper processes behind every tab and every window, with long and messy launch arguments. They match very easily. One command, and all of them were gone.

I had meant to add a layer of safety, and that layer turned into the bomb.

What I changed

First, stop dev servers by port, not by guessing at names.

lsof -ti :3000 -sTCP:LISTEN | xargs kill

Whatever is listening on that port gets killed, and nothing else gets hurt.

Second, if you really must use pkill, put every option before the pattern, and make the pattern specific down to a port or a path. Before acting, preview with pgrep using the same arguments:

pgrep -fl "next start -p 3011"
pkill -f "next start -p 3011"

Look at what it would hit first, then act.

Third, and to me the most important: turn it into a hook, and do not stop at writing a rule.

A rule is a request; a hook is a guarantee

I fell into the same hole twice. Today, the global rules file I give Claude Code, CLAUDE.md, has a whole section on stopping processes, and both incident dates, September 7 and September 14, are written down in it.

After the second time, I stopped counting on anyone "remembering". Using Claude Code's PreToolUse hook, I wrote a Python script of a little over two hundred lines. Before the AI runs any Bash command, the script checks it, and if pkill or pgrep has an option written after the pattern, the command is blocked and never runs.

This made one distinction clear to me.

Anything written in a prompt or a rules file is a "request" as far as the AI is concerned. It will very likely follow it, but once the context gets long or the task gets urgent, there is a chance it slips.

Anything written into a hook is a "guarantee". It does not depend on whether the AI remembers or understands; the command never reaches the shell.

Managing a team works the same way. Important things should not rely on "everyone, please be careful"; they should be enforced by the process. If CI can block it, do not rely on code review. If permissions can block it, do not rely on self-discipline.

Letting AI run commands directly on your own machine is an everyday thing now. Its mistakes are not necessarily worse than a person's, but it makes them much faster. While you set rules for it, remember to install guardrails too.

As for me, every time I see pkill now, I check the argument order by reflex.

Two rounds of wiped-out tabs in exchange for that bit of muscle memory. Not too bad a trade.

Keep reading