ai newsDi., 3. Nov.
EilmeldungGenerative media inference startup fal raises $1.3B at a $16B valuation

Claude Code killed its own shell with pkill -f (exit 144): why, and a 2-minute fix

You ask Claude Code to restart a dev server. It runs: pkill -f server.js && node server.js & The tool call ends with exit 144 (sometimes 143). Nothing after pkill runs. The server is still the old one, or there is no server at all. If the agent used a wait loop instead, while pgrep -f server.js; do

dev.toDienstag 22:26 · 03.11.4 Min. Lesezeit
Bild: dev.to

You ask Claude Code to restart a dev server. It runs:

pkill -f server.js && node server.js & Enter fullscreen mode Exit fullscreen mode The tool call ends with exit 144 (sometimes 143). Nothing after pkill runs. The server is still the old one, or there is no server at all. If the agent used a wait loop instead, while pgrep -f server.js; do sleep 1; done never ends.

This is anthropics/claude-code#93607. We wrote it down as a rule, then hit it several more times in two days, so we stopped it mechanically. Here is what happens and how to fix it.

pkill -f matches the pattern against the full command line of every process, not just the program name.

Claude Code's Bash tool runs your command inside a shell, and that shell's own command line contains your whole command: the tool's wrapper plus pkill -f server.js && node server.js &. So the string server.js is in the shell's command line. pkill finds a match, and the match is the shell that is running pkill. The shell dies, and so does the tool call.

In a normal terminal you rarely see this, because your interactive shell's command line is just bash. In an agent, every command is wrapped, so the pattern is always present.

1. pkill -A -f (best, if you have it). -A tells pkill to ignore its own ancestors, so the shell running it is skipped. It needs procps-ng 4.0.3 or newer (check with pkill --version ). On Claude Code 2.1.316 on Linux, pkill -A -f server.js killed the server and the shell survived.

pkill -A -f server.js Enter fullscreen mode Exit fullscreen mode 2. The bracket trick, with a catch. pkill -f '[s]erver.js' works because the regex [s]erver.js matches the text server.js but not the text [s]erver.js. The catch, pointed out in the issue thread: if the plain word appears anywhere else in the command, for example cd ~/server.js-app && pkill -f '[s]erver.js', the shell matches again and dies.

3. A PID file. Write the PID when you start the server ( node server.js & echo $! >.server.pid ) and stop it with kill "$(cat.server.pid)". No pattern, no self-match. This is also the fix for BusyBox and older procps, which have no -A.

Telling Claude "don't use pkill -f" in CLAUDE.md did not hold. The pattern came back during debugging, when the agent was focused on something else. A rule the agent has to remember is a rule it will eventually forget.

So we turned it into a PreToolUse hook: before a Bash command runs, check whether a pkill -f / pidwait -f pattern (or a pgrep -f whose result is used) would match the command's own text, and deny it with the fix if -A is missing. Claude reads the reason and rewrites the command itself.

It is packaged as a plugin. Two lines in Claude Code:

/plugin marketplace add hadiux0295/claude-code-mistake-guards /plugin install synura-mistake-guards@synuracore Enter fullscreen mode Exit fullscreen mode What it does and does not do:

We tested it on our own session logs before shipping: of 121 real pkill -f calls, it would have denied 61. The other 60 were bracketed patterns that really don't self-match, or the word appearing only inside text. The plugin ships with 37 tests of its own ( test_plugin.sh ), all passing.

Credit: the -A fix and the bracket edge case come from gweber and corneliusroemer in the #93607 thread. The plugin only packages that check, with tests.

This bug is a good example of something we kept seeing in five months of daily agent work: the mistakes that repeat are the ones that look like success. Exit 144 looks like a flaky command. A wait loop that never ends looks like a slow server. The agent moves on.

The repo has six more guards like this one (bare git pull in a shared tree, saves that overwrite a record with a placeholder, replies that read bot traffic as demand, and more), each with the incident it came from: github.com/hadiux0295/claude-code-mistake-guards. There are also 50 of the mistakes in English, each with a one-line CLAUDE.md rule.

Who wrote this: the incident records were written by the AI that made the mistakes (Claude Code), as it worked. This post and the tests were written by AI (Claude) and checked against the session logs and the issue thread. A human set the rules and runs the setup.

Quelle: dev.to (dev.to). Original öffnen