Claude Code permission errors: what each message means

What each Claude Code blocking message means (bash denied by auto mode, deny rules, hooks) and the exact setting that unblocks it.

Claude Code permission errors: what each message means

When Claude Code blocks an action, three layers could have done it: a deny or ask rule of yours, the auto mode classifier, or a PreToolUse hook. The wording of the message tells you which one, and each unblocks in a different place. Here is each message with its fix.

bash denied by auto mode · [Data Exfiltration] · /permissions

That notice comes from the auto mode classifier, not from a rule of yours. In auto mode, anything that is not a read or an edit inside the working directory goes through a second model that decides whether the action is safe, and when it says no, Claude Code shows that notice next to the input box [1]. What sits in brackets is the label of the classifier rule that matched, for example [Data Exfiltration] or [Production Deploy]. Below the call you will also see the line Denied by auto mode classifier [1].

The notice does not include the command. To see it, find the call in the conversation and press Ctrl+O if it appears folded. To review and retry, open /permissions and go to the Recently denied tab: the r key marks an action for Claude to try again when you close the dialog [1].

If what got blocked is a destination you will keep hitting all through the task (your private package registry, an internal domain, the host for your repos), describe it in autoMode.environment:

{
  "autoMode": {
    "environment": [
      "$defaults",
      "Source control: github.example.com/acme-corp and all repos under it",
      "Internal package registry: npm.example.com"
    ]
  }
}

That block goes in ~/.claude/settings.json, in managed settings, or behind --settings, never in the project settings: the classifier does not read it from there, so that a cloned repository cannot grant itself permissions [2]. Check what is actually being applied with claude auto-mode config. How to describe your infrastructure properly in there, and why the "$defaults" token is not decorative, is covered in the guide to autoMode.environment.

When you should not unblock it: if the block appeared while pushing to a host that is not yours, the classifier is doing its job. Adding that host to environment declares it trusted for good, not just this once.

When the message names the tool and the rule

If the notice names the tool and, when the rule carries a specifier, the rule that matched as well, the block comes from a deny rule of yours or of your organization, evaluated before the classifier gets to see anything [3].

The usual mistake here is not the message, it is what you do next: adding a narrower allow. It does not work, because rules are evaluated in the order deny, then ask, then allow, and specificity does not change that order [3]. The fix is to remove or narrow the deny, in whichever file it lives; /permissions shows you the active rules and where they come from. How to write those rules so they protect what you actually want is in the permission model guide.

If the rule comes from managed settings, there is nothing you can do from your side: no level, not even command-line flags, overrides a managed permission rule [3].

allowManagedPermissionRulesOnly: why your rules stopped counting

If you searched for this name it is because you found it in a file at your company, or because your allow rules stopped taking effect overnight. It is a key that only managed settings can set, and it makes managed settings the single source of permission rules [4]. While it is on, Claude Code discards allow rules coming from a settings file, from --allowedTools or from the host application [4].

This is not a setting you negotiate locally. The fix is to ask whoever administers the fleet to add the rule to the managed source, which on macOS is /Library/Application Support/ClaudeCode/managed-settings.json and on Linux or WSL /etc/claude-code/managed-settings.json [4].

useAutoModeDuringPlan: why plan mode runs commands without asking

With auto mode available and useAutoModeDuringPlan on, which is how it ships, plan mode has the classifier review shell commands instead of asking you: the ones it approves run and the ones it rejects are blocked [5]. If you would rather plan mode asked you, set it to false:

{
  "useAutoModeDuringPlan": false
}

And here is the detail that costs you an afternoon: the file matters. Claude Code honours a false from any managed source, from --settings, from ~/.claude/settings.json and from .claude/settings.local.json, even when the winning managed source says true. But a false in .claude/settings.json is ignored [6]. If you put it in the repository’s shared settings, the field name is right and nothing happens anyway.

Denied by preToolUse hook from "repo settings" (hook errored)

This message does not come from Claude Code. It comes from Copilot CLI, which reads the hooks in your .claude/settings.json and runs them under a different contract [7]. In the report of this failure, which is on Windows, hooks are launched with PowerShell instead of bash and the $CLAUDE_PROJECT_DIR variable comes out empty, so a hook written for Claude Code blows up on startup; and since the failure is treated as a denial, it blocks every tool call [7]. The log says it more clearly than the notice does: preToolUse hook from "repo settings" execution failed (fail-closed).

If this happens to you, check where the hook is being run before you touch its code. Run the same command by hand from the project root: if it works there, the problem is the shell or the directory it gets invoked from.

In Claude Code a hook block shows up as Execution stopped by PreToolUse hook, followed by whatever the hook wrote. A hook that exits with code 2 cuts the call before permission rules are evaluated, so it blocks even if you have an allow that would approve it [3]. To rule the hook out for a single run, start with --settings '{"disableAllHooks": true}'; putting it only in your user settings is not enough, because the project settings carry more weight and can set it back to false [3].

How to tell which of the three layers blocked you

What you seeWho blocked youWhere it gets fixed
... denied by auto mode with a label in brackets, or Denied by auto mode classifierThe auto mode classifierautoMode.environment or an allow rule, in ~/.claude/settings.json. Also from the Auto mode tab in /permissions
Permission to use Bash has been denied.A deny ruleWhichever file that rule lives in. If it is managed, your administrator
Execution stopped by PreToolUse hookA hook of yoursThe hook script

When the message says the classifier model is temporarily unavailable, there is nothing to configure: Claude Code blocked the call without a verdict, and those cases are not even recorded under Recently denied [1].

If what you want is not to unblock a single message but to write down once and for all what your agent may and may not do, the full permission model guide covers rule syntax and which file each lever belongs in, and the auto mode configuration goes deep into autoMode.environment. Deciding where to draw an agent’s boundary before you let it run is exactly what you practise in the Design Patterns for AI Agents course.

Sources

  1. Configure auto mode — Review denials — the literal text of the bash denied by auto mode · [Data Exfiltration] · /permissions notice, the Denied by auto mode classifier line, the Recently denied tab and the unavailable-classifier case.
  2. Configure auto mode — Where the classifier reads configuration — the scopes the classifier reads, the exclusion of project files, and the "$defaults" token.
  3. Configure permissions — deny → ask → allow precedence, the Permission to use ... has been denied. message, hooks with exit code 2 and --settings '{"disableAllHooks": true}'.
  4. Deploy managed settings — Managed-only settings — allowManagedPermissionRulesOnly, which sources it discards and the managed-settings.json paths per system.
  5. Choose a permission mode — how useAutoModeDuringPlan behaves in plan mode, and permissions.disableAutoMode.
  6. Settings files and precedence — Exceptions to managed settings precedence — which files a false in useAutoModeDuringPlan is honoured from, and which one is ignored.
  7. github/copilot-cli issue #4001 — the Denied by preToolUse hook from "repo settings" (hook errored) message in Copilot CLI, with the log trace and the cause.

Frequently Asked Questions

How do I turn auto mode off completely?

By setting permissions.disableAutoMode to "disable" in any settings file. That takes auto out of the Shift+Tab cycle and makes a session started with --permission-mode auto begin in Manual instead. A session already in auto mode leaves it when the setting reaches it from an administrator-deployed source, and shows auto mode disabled by settings.

Why isn’t my allow rule working?

Because there is a deny or an ask matching the same call. Rules are evaluated in the order deny, then ask, then allow, and the first one that matches in that order wins, regardless of which is more specific. A narrow allow does not open an exception inside a broad deny.

Can I get around a permission rule my organization set?

No. Managed settings sit above everything else, and no level, command-line arguments included, overrides a managed permission rule. And if your organization also has allowManagedPermissionRulesOnly on, your allow rules are discarded as they are read.

What happens if a hook fails?

It is treated as a denial. A PreToolUse hook that exits with code 2 cuts the call before permission rules are evaluated, so the block applies even if you have an allow rule that would let it through. A hook that does not even start (bad path, wrong shell) produces the same effect with a far less clear message, and that is the cause behind Copilot CLI’s (hook errored).

Can hooks approve something a deny rule forbids?

No. A hook’s decisions do not skip permission rules: Claude Code evaluates deny and ask no matter what, so a matching deny rule blocks the call even if the hook returned "allow", and a matching ask still prompts you. Precedence only works in one direction: a hook can block more, never less.