cd /news/ai-agents/i-wrote-a-safety-mod-that-crashes-on… · home › topics › ai-agents › article
[ARTICLE · art-145511] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=↓ negative

I wrote a safety mod that crashes on purpose. Claude Code ran the command anyway.

A developer tested ten community safety and utility mods for Claude Code against 85 sessions, 882 prompts and 5,993 tool calls on version 2.1.288, finding that a guard hook which throws is silently skipped by default, so the guarded command still executes. Adding a single .catch handler to the tool.call hook makes the same crashing guard fail closed, blocking the command and reporting the denial to the model. The testing also measured per-turn overhead for plugins such as next-steps (about 250 extra output tokens and 2.9 seconds per answer) and found that its session fork fires even in headless runs where suggestions cannot be displayed.

by read5 min views4 publishedOct 5, 2026

I wrote the dumbest safety mod I could think of. It watches every shell command Claude Code is about to run, and it throws.

Then I asked for one command:

touch ./marker-failopen.txt

The guard crashed, as designed. One line showed up in the log: failguard: tool.call hook skipped: threw Error: guard crashed. And the file was sitting in the folder.

So what does a guard mod protect you from when the guard itself breaks? By default, nothing. That was the part of a week of testing that stuck with me most, and it is one of the reasons I deleted seven of the ten mods everyone told me to install.

Mods shipped in Claude Code at the start of October: TypeScript functions inside a plugin that hook into an event and run before it, after it, or instead of it. Within a day, three tours were out telling people to install ten of them. None of them measured anything.

I took those ten and ran them against my own week: 85 sessions across 4 projects, 882 prompts, 5,993 tool calls. Each mod went through claude plugin validate, then ran the same task against a clean baseline on Claude Code 2.1.288, same machine, medians of three runs.

Six of the ten cost nothing I could measure at runtime. That does not make all six worth keeping.

This is documented, not a bug. The events page says that when a hook with no .catch handler throws, times out, or returns a result of the wrong shape before calling next, "Claude Code skips it, and the next handler runs in its place."

It makes sense for decoration. A broken heatmap should not brick your session. For a guard it means the failure is silent and the command goes through.

The fix is in the same docs, and it is one handler:

on('tool.call', { tool: 'Bash' }, guard).catch(async ($, e, next) => { return { deny: 'The command guard failed, so this command was not run: ' + next.error.kind } })

Same crashing guard, same prompt, plus that catch: no file, and the model reports the block. One line decides whether your guard fails open or fails closed. If you installed a safety mod this week, open it and look for that catch.

A community re-test on 2.1.288 went further, 10 cases with 3 runs each, judged by marker files and not by what the model said. The catch pattern held. One path is still open: a deny returned after the call was already forwarded does not stop the tool. The file landed 3 times out of 3 while the model was told the write failed.

next-steps is the one every video opens with. Your answer finishes, three suggested prompts appear above the composer, you press a number.

To get those suggestions it forks the session after each turn. Its README says so plainly: the fork shares the prompt cache, "so it costs about one short reply." On my bench that came to about 250 extra output tokens and 2.9 seconds per answer.

config duration output tokens
baseline 3980 ms 247
next-steps 6830 ms 497
cache-keeper 5569 ms 367

The detail I did not expect: the fork has no surface gate. Suggestions only draw in a terminal, but I watched the fork fire in a headless claude -p run where nothing can be drawn. There is no setting that keeps the suggestions and drops the fork. You can raise the answer length threshold or disable the plugin.

cache-keeper has the same shape, about 1.6 seconds per turn, plus paid pings to keep a cache warm that already lasts an hour on a subscription plan.

claude plugin validate prints what a mod hooks, what it calls and which environment variables it reads. It takes a couple of minutes and it runs before you enable anything.

session-bookmarks was one of my six free mods at runtime. Its audit lists $.model.complete, $.process.run and $.fs.write. A bookmark that can call the model, run programs and write files. Nothing in it is malicious. It is just a lot of reach for the job, and I would not have known without reading two lines of output.

This matters because of what Anthropic said on launch day: "Mods run with the same access to your machine as Claude Code itself. They aren't sandboxed." Your permission rules cover Claude's tool calls, not the mod's own. The docs put it in one sentence: "with Read(.env) denied, a mod can still read that file with $.fs.read or start a program that does."

Claude Code already had hooks, a shell script that fires on the same events. The measurable difference is the process spawn on every call:

hook body per call over 5,993 calls
bash -c 'exit 0' 8.3 ms 50 s
python3 -c 'pass' 26.1 ms 156 s
node -e '' 43.1 ms 258 s

A mod runs in-process and pays none of that. But four minutes a week only shows up at thousands of calls. My rule now: interface or event rewriting is a mod. Block, allow or log with a script you have read is a hook. A hook you have read beats a mod you have not.

Three out of ten.

One week, one machine, my workload, three runs per point on the small model. Two rows in my table moved in ways I put down to answer variance, so I only quote the deltas that held. Your three keepers might differ. The method transfers better than the list.

The video shows the crashing guard and the fixed one side by side, and the full ten-row verdict table with the measured cost of each mod.

Which mods are still on your machine after the first week? And did any of your guards have the catch handler when you checked?

I use AI tools to help tighten my drafts. The runs, the numbers and the opinions are mine.

── more in #ai-agents 4 stories · sorted by recency
── more on @claude code 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/i-wrote-a-safety-mod…] indexed:0 read:5min 2026-10-05 · —