DEV Community

Cover image for Claude Code's new safety_event finally shows which guardrail blocked your agent
Piekwerk
Piekwerk

Posted on

Claude Code's new safety_event finally shows which guardrail blocked your agent

Every team that runs Claude Code with real permission rules eventually has the same argument. Someone asks why the agent did not do the thing, and you start guessing: was that tool call blocked by a permission rule, by auto mode, by a hook, or by the sandbox? Until now the answer lived in session logs you had to grep by hand. Version 2.1.307, one of the two releases that landed on October 21, changes that. The changelog line is dry, but it matters if you maintain agent config:

Added the claude_code.safety_event OpenTelemetry event for each tool call a permission rule, auto mode, a safety check, a hook or the sandbox stopped, prompted or sent back

One event, five possible sources, three outcomes. That is the difference between arguing about where enforcement belongs and measuring where it actually fires.

What the event actually tells you

The event fires per blocked tool call, and the interesting dimension is the source. A permission rule firing a hundred times a day means your allowlist does not match the work. A hook firing constantly means your hook is doing policy work that probably belongs in config. The sandbox showing up constantly on Linux builds might trace back to sandbox.allowNestedUserNamespaces, added in 2.1.305, which the changelog itself notes lets sandboxed commands get around the sandbox's file protections when a browser or rootless podman needs a user namespace.

The outcome field matters too. "Stopped" is a hard block. "Prompted" means a human got asked, which is a friction cost even when the human answers correctly every time. "Sent back" means the agent had to redo work. Three different failure modes, three different fixes.

The same release also fixed /permissions > Recently denied so it now shows the denied shell command instead of its description. So even without a telemetry backend you get a slightly better manual answer to "what did I just block".

Turning it on takes five environment variables

The event rides the standard Claude Code telemetry pipeline, documented in the monitoring usage docs:

export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer your-token"
Enter fullscreen mode Exit fullscreen mode

Point the endpoint at an OpenTelemetry Collector, or directly at whatever accepts OTLP in your stack. To verify the pipeline before you look for safety events, check your backend for the claude_code.session.count metric when a session starts, or the claude_code.user_prompt event after your first prompt. Logs export every 5 seconds by default, metrics every 60.

One detail from the docs that bit me before: Claude Code does not pass OTEL_* variables to subprocesses it spawns, including the Bash tool, hooks, and MCP servers. Your own instrumented app run through the Bash tool will not inherit the collector endpoint, so set those variables in the command itself.

What to measure once it flows

The first useful query is a simple group-by: safety events per source, per day, per repo. I would look at three things.

First, the ratio of prompted to stopped. Every prompt is a human interruption. If one permission rule generates most of the prompts and the team always approves it, the rule is wrong, not the team.

Second, which tools get blocked most. A cluster around file writes outside the worktree points at a scope mismatch. A cluster around Bash commands with pipes or subshells usually means your pattern matching is too literal, because the agent composes commands no human would type.

Third, hook-sourced events over time. I wrote before about where agent enforcement actually belongs, and the honest answer was "it depends on what fails safest". This event turns that from an opinion into a per-repo measurement: if hooks block ten times what your permission rules block, your policy lives in the wrong layer.

The MCP bug that will pollute your first week of data

v2.1.306, shipped the same day, fixed something that will confuse anyone measuring on older versions: in sessions with many MCP tools, an API error was being replaced with "prompt is too long", which then triggered an unneeded compaction. If you are on a pre-306 build and you see constant compaction in an MCP-heavy repo, your context is probably fine and your tool count is the problem. Every attached MCP server injects tool definitions the model has to carry; the same budget logic I use for instruction files applies to tool schemas, and I apply a hard audit before adding a server.

Related fix in 2.1.307: HTTP MCP tool calls that were being sent twice when a connection closed mid-call are now only retried when the tool is read-only or idempotent. If you saw mysterious duplicate side effects from write-capable MCP tools, that was not your imagination.

Honest limits

The event tells you a call was blocked and by which layer. It does not tell you whether the rule was right, and it will not stop a team from approving its way around a bad rule. The detail flags that put Bash command text and tool parameters into events, like OTEL_LOG_TOOL_DETAILS, default to off, which is correct: command text in a telemetry backend is a data-exposure decision, not a default. And export failures are silent by default in SDK setups, so confirm the session metric arrives before you conclude the event does not fire.

What I would do this week

Enable telemetry on one repo, point it at a collector, and let it run for a day of normal agent work. Then read the top ten blocked tool calls per source. In my experience the first read always surfaces one permission rule nobody understood and one hook that fires far more than its author expected. Fix or move those, then re-measure. That loop, rule change to measured outcome, is the whole point of shipping this event, and it was impossible before October 21.

Top comments (0)