← Back to Blog

When Claude Code's Auto Mode Blocks Only Bash: Investigating the Safety Classifier Outage, Plus a Fail-Open-on-Outage Hook Design

Part of Claude Code Field Guide

One day, while working in Claude Code’s auto mode (automatic approval), the Bash tool started failing intermittently. The error looked like this:

Error: <model> is temporarily unavailable, so auto mode cannot determine
the safety of Bash right now. Wait briefly and then try this action again.
If it keeps failing, continue with other tasks that don't require this
action and come back to it later. Note: reading files, searching code,
and other read-only operations do not require the classifier and can
still be used.

Reading and writing files worked. Code search worked. But I couldn’t run a single git status. Retries sometimes went through; sometimes nothing worked for minutes. This post is a record of what I found when I dug into the cause, plus a hook design that changes behavior only during an outage.

What was actually happening

In auto mode, the safety of each command Claude wants to run is decided in two stages.

Stage one is the permission rules in settings.json. If a command matches a deterministic rule like Bash(git status), the allow/deny decision happens right there.

Stage two is the main character of this story: a server-side safety classifier. Commands that the rules can’t decide get sent to a judgment model running on Anthropic’s side. Safe commands are auto-approved; dangerous ones are blocked. This judgment is the reason auto mode can exist without silently running rm -rf for you.

The problem is what happens when the classifier goes down. If it can’t judge, Claude Code fails closed: it errs on the side of safety and blocks the affected tools entirely. “If we can’t judge it, don’t run it” is the correct call for a safety system. But the classifier depends on upstream model availability, so when an outage bursts during peak hours, Bash becomes unusable for the duration.

The last line of the error message was the clue: “read-only operations do not require the classifier.” Read-only tools like Read and Grep skip the classifier by design, which is why they kept working through the outage. Only tools with side effects go through judgment, and that’s why Bash looked like it was being singled out.

What I could observe

Grepping through that day’s session log (the transcript) afterwards, the facts lined up like this:

  • Bash was blocked, and, briefly, so was Write. Consistent with “tools with side effects get judged”
  • Read, Edit, and Grep-via-subagent ran untouched all day
  • Turning off auto mode restored everything instantly. It falls back to the normal approval prompt, where a human approves, so the classifier is out of the loop

The conclusion: a server-side availability incident, with nothing to fix on my machine.

It was a known issue

Searching for the error message turned up multiple reports in the official repository:

  • #74949 (OPEN): outages bursting during peak windows, with fail-closed blocking nearly all compound commands. Exactly my experience
  • #68437 (CLOSED): the classifier reporting “temporarily unavailable” while normal generation works fine

The sharp observation in #74949: compound commands containing && or | can’t be fully evaluated by static permission rules, so no matter how many allow rules you write, they go to the classifier anyway. The naive fix of “just register your frequent commands as allow rules” only helps single commands. Most of my real usage looks like cd hoge && npm test, so that lever barely moves anything.

The options on the table

From what I found, there are three user-side options:

OptionEffectLimitation
Register frequent commands in permissions.allowMatched commands skip the classifierCompound commands can’t be statically evaluated; they go to the classifier anyway
Manually turn off auto mode during outagesReliable recoveryYou have to notice and toggle. Outages are intermittent, so you keep flipping back and forth
Return your own judgment from a PreToolUse hookIf the hook returns allow, the classifier is never reachedThe bypass is permanent, not outage-only

The third option looks clean at first glance. A PreToolUse hook runs an arbitrary script before tool execution and can return a decision as JSON: permissionDecision: "allow" bypasses the entire permission flow and executes immediately, "deny" blocks, "ask" escalates to the human, and returning nothing proceeds to the normal flow.

Write a script that returns ask for a denylist of dangerous patterns and allow for everything else, and you effectively have a local classifier, immune to server outages.

But there’s a fundamental problem: the bypass also applies when the classifier is healthy. Anthropic’s judgment model reads context far better than a handwritten denylist. Throwing away that judgment during normal operation to defend against occasional outages felt backwards.

What I actually wanted was a conditional: classifier when it’s up, local judgment only when it’s down.

Is fail-open-on-outage even possible?

For the conditional to work, the hook needs to know the classifier is currently down. There’s no API to query its health. I almost gave up there, but one indirect detection channel exists.

Hooks receive transcript_path on stdin. That’s the path to the session’s conversation log (JSONL), and when a tool gets blocked by a classifier outage, the error message itself is recorded in the transcript. The error text at the top of this post came straight out of that day’s transcript via grep.

So the hook can work like this:

  1. Normal operation: no error traces in the transcript → return nothing. Proceed to the normal flow (the classifier). Zero behavior change
  2. A “cannot determine the safety” error found within the last 10 minutes of the transcript → outage mode. Return allow unless the command matches the denylist
  3. Ten minutes without a new error → automatically back to normal behavior

Since detection depends on “having been blocked once,” the first shot always fails. But Claude retries blocked commands, so in practice this becomes “goes through from the second attempt.” Take the first fail-closed hit, ride out the rest of the burst on local judgment. A compromise, but a workable one.

Design sketch

Still unverified, at the design stage, but the skeleton of the hook script looks like this:

#!/bin/bash
# classifier-outage-fallback.sh — PreToolUse (matcher: Bash)
set -euo pipefail
input=$(cat)
transcript=$(jq -r '.transcript_path' <<<"$input")
cmd=$(jq -r '.tool_input.command // empty' <<<"$input")

outage() {
  # any classifier-down error within the last 10 minutes of the transcript?
  tail -n 400 "$transcript" 2>/dev/null \
    | jq -c 'select(.timestamp? and
        ((.timestamp | fromdateiso8601) > (now - 600)))' 2>/dev/null \
    | grep -q 'cannot determine the safety'
}

dangerous() {
  grep -Eq 'rm +-rf|--force|--no-verify|reset +--hard|-fd?D' <<<"$cmd"
}

if ! outage; then
  exit 0  # normal operation: return no decision, defer to the classifier
fi

if dangerous; then
  jq -n '{hookSpecificOutput: {hookEventName: "PreToolUse",
    permissionDecision: "ask",
    permissionDecisionReason: "classifier outage: dangerous pattern, ask human"}}'
else
  jq -n '{hookSpecificOutput: {hookEventName: "PreToolUse",
    permissionDecision: "allow",
    permissionDecisionReason: "classifier outage: not on denylist, provisional allow"}}'
fi

Registration goes in settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/classifier-outage-fallback.sh"
          }
        ]
      }
    ]
  }
}

If I do adopt this, the plan is staged: run it warn-only for a few days first, logging decisions without intervening, and enable it only after confirming there are no false judgments.

The weaknesses, stated up front

I’m aware of three holes in this design.

The error-string match is brittle. Detection hinges on the literal string “cannot determine the safety,” so a Claude Code update that rewords the message silently kills detection. The saving grace: in that case the hook just reverts to normal behavior (fail-closed). It never fails toward danger. The failure mode itself is safe.

Transcript writes are asynchronous. The official docs state that the transcript file is written asynchronously and may lag the in-memory conversation. There can be moments when the most recent error hasn’t hit the file yet, so detection may lag by seconds to tens of seconds. Outage bursts last minutes, so I expect the practical impact to be small, but taking the second hit as well as the first will happen sometimes.

The denylist’s quality becomes your safety. During outage mode, handwritten regexes stand in for the classifier. They catch the obvious patterns like rm -rf and --force, but not context-dependent dangers, like a redirect overwriting an important file. This only works if you treat it as a temporary regime during outages and keep the denylist conservatively fat.

Not adopting it yet

After all that design work, I haven’t installed the hook. The reason is simple: I don’t know whether the outage frequency will persist.

Server-side problems are best fixed server-side, and the fact that #68437 is CLOSED suggests improvements are landing. So first: update the CLI to the latest version and watch for a few days. If I still hit outages routinely, roll out the hook starting from warn-only. The countermeasure has its own costs (maintenance, denylist upkeep, tracking upstream message changes), so I’ll see the actual frequency before paying them.

I ended up designing a whole mitigation out of spite after losing a day to this, but the real takeaway was understanding the machinery. Auto mode’s safety judgment depends on a server-side model and is protected by fail-closed. Read-only tools skipping judgment, the error message carrying its own workaround hints: once you know the design, it all hangs together sensibly. As long as it stays up.

Summary

  • Auto mode’s Bash blocks were caused by a server-side safety classifier outage plus fail-closed design. Nothing to fix locally
  • Read-only tools skip the classifier and keep working; turning off auto mode falls back to human approval and recovers immediately
  • Allow rules don’t help compound commands (#74949)
  • A PreToolUse hook watching the transcript can implement “fail-open only during outages” with zero change to normal behavior
  • But adoption waits until after a CLI update and a re-observation window. Countermeasures have costs too
Harness Engineering Related book Harness Engineering Five interpretations from OpenAI, Anthropic, LangChain, Martin Fowler, and academia — merged into one system for engineers running AI agents in production View the book page →