Auto mode classifier blocks tool calls while session is in bypassPermissions

Status Open
Reported on v2.1.223
Maintainer reply None cached
Activity 0 comments · opened Aug 6, 2026

Environment

  • Claude Code 2.1.223 (also observed on 2.1.221)
  • Native install, macOS (Darwin 25.x), Apple Silicon
  • permissions.defaultMode: "auto" in user settings; sessions switched to bypassPermissions interactively
  • No managed policy settings present; disableBypassPermissionsMode not set
  • No autoMode section in user settings — shipped default rules throughout

Summary

In sessions running in bypassPermissions, tool calls are intermittently refused by the auto mode classifier with toolDenialKind: "automode-blocked".

The permission-modes documentation describes bypassPermissions as skipping permission checks. If that is accurate, the classifier should not be consulted in this mode at all, and this is a bug. If instead the classifier is retained in bypassPermissions by design — the way PreToolUse hooks are — then the behavior is correct but undocumented, and the documentation should say so, because the current wording leads operators to conclude the mode is broken.

Either way the operator-visible outcome is the same: a mode chosen precisely to stop interruptions still interrupts.

Evidence

Across a 50-session window, 6 sessions ran in bypassPermissions. Four automode-blocked denials occurred inside those sessions. In each case:

  • The denial entry is main-loop, not a subagent (isSidechain: false), so this is not a subagent inheriting a different mode.
  • The most recent permissionMode value recorded before the denial — also main-loop — is bypassPermissions.
  • The session continues emitting bypassPermissions afterwards, so the mode had not been switched away and back.

One session's mode/denial sequence, with consecutive identical events collapsed:

bypassPermissions x76  ->  DENIED: automode-blocked  ->  bypassPermissions x42

A second session shows the same shape: 46 consecutive bypassPermissions events, then automode-blocked.

The blocked calls were ordinary read-only operations, including gh pr view <n> --json number,mergeStateStatus,reviewDecision. That command matches the shipped Read-Only Operations allow rule, so the block is also hard to attribute to any specific deny rule.

Steps to reproduce

Not reliably reproducible on demand — the intermittency is part of the report. Observed conditions:

  1. Set permissions.defaultMode to "auto" in ~/.claude/settings.json.
  2. Start a session, switch it to bypassPermissions.
  3. Run a long session with many tool calls, including /model switches and /compact.
  4. Intermittently a tool call is refused with automode-blocked while the mode is still bypassPermissions.

Denial records live in the session transcript JSONL: user entries carrying a top-level toolDenialKind, interleaved with entries carrying permissionMode.

Expected

In bypassPermissions, no permission check runs and no classifier is consulted; a tool call executes or fails on its own merits.

Alternatively, the documentation states plainly that the auto mode classifier and PreToolUse hooks both still apply in bypassPermissions.

Actual

The classifier runs and can refuse the call, producing a denial the operator explicitly opted out of.

Impact

This is the difference between an agent that can run a multi-hour unattended task and one that cannot. Long-horizon autonomous work is the use case bypassPermissions exists for, and an intermittent block halts the run at an arbitrary point, often on a trivially safe read.

Documentation suggestions (minor, separable)

  1. skipDangerousModePermissionPrompt: true reads as though it forces bypass mode. It suppresses the confirmation dialog only. One clarifying line in the settings reference would prevent the misreading.
  1. Hooks override bypassPermissions, and the permissions page does not say so. A PreToolUse hook returning permissionDecision: "ask" prompts even in bypassPermissions. That is defensible — it lets a project keep a hard safety net the operator cannot switch off by accident — but it is the first thing an operator suspects when bypass "does not work", and the remedy (reading the permission_mode field inside the hook and honoring it) is discoverable only from the hooks reference. Suggest cross-linking permission_mode from the permission-modes section.
  1. Denial messages do not name the rule that fired. A refusal reads "Blocked by classifier" with no rule name, and its remedy line suggests adding a Bash permission rule even when the denied call was an Edit. Naming the matched rule — e.g. "Self-Modification" — would tell the operator what consent is missing. As it stands, the only way to find out is to run claude auto-mode config and match the action against the rules by hand. For the soft_deny family this matters a lot: those rules clear when the user names the specific change, so a named rule is directly actionable.

View original on GitHub ↗