anthropics / anthropics/claude-code

[BUG] Auto-mode classifier denials still append tool-substitution coaching ("use head instead of cat") on deliberate blocks — the refusal path already dropped it

Abierto
#92,411 1 comentario 0 reacciones 0 asignados Ver en GitHub
area:permissions area:security bug has repro platform:wsl
Lenguaje dominante
Python
Estrellas
145k
Forks
23.1k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

### Preflight Checklist

- [x] I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue+state%3Aopen+label%3Abug) and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code

### What's Wrong?

An auto-mode classifier denial that blocks an action still appends a sentence teaching the agent to retry the same goal through a different tool:

> IMPORTANT: You \*may\* attempt to accomplish this action using other tools that might naturally be used to accomplish this goal, e.g. using `head` instead of `cat`.

A denial that ships the workaround inside itself weakens the control it is enforcing, and it trains agents to treat permission gates as things you route around rather than stop at.

This is the surviving half of #74301, which was auto-closed as stale on 2026-09-05 with no maintainer response. I am re-filing **only** this part, narrowly, because the other half has since been fixed — see below.

### What Should Happen?

The blocked-by-classifier denial should state the reason and stop, keeping the parts that are useful and true — "continue with other tasks that don't require this action", "if it is essential, STOP and explain to the user", and the permission-rule pointer — and dropping the tool-substitution instruction.

Concretely: the substitution hint should be **selected by block category** rather than appended to every denial, in the same way the refusal path selects its own text from the decision object.

I want to be explicit that a blanket removal is *not* obviously right, because the hint sometimes does real work: `head` instead of `cat` is not only a way around the gate, it genuinely reduces how much of a file is exposed, so for shape-triggered blocks the current sentence can be steering toward the safer action. The case for changing it rests on the blocks where that is not true — where the classifier blocked the goal, no substitution can satisfy it, and the sentence sends the agent through identical denials. Category selection resolves both; universal removal only resolves one and regresses the other.

### Error Messages/Logs

```shell
Permission for this action was denied by the Claude Code auto mode classifier.
Reason: Blocked by classifier. If you have other tasks that don't depend on this
action, continue working on those. IMPORTANT: You *may* attempt to accomplish this
action using other tools that might naturally be used to accomplish this goal, e.g.
using head instead of cat. But you *should not* attempt to work around this denial
in malicious ways, e.g. do not use your ability to run tests to execute non-test
actions. You should only try to work around this restriction in reasonable ways
that do not attempt to bypass the intent behind this denial. If you believe this
capability is essential to complete the user's request, STOP and explain to the
user what you were trying to do and why you need this permission. Let the user
decide how to proceed. To allow this type of action in the future, the user can
add a Bash permission rule to their settings.
```

### Steps to Reproduce

Verbatim denial received in a normal interactive session on 2.1.261 (a `dd`/`grep` loop reading a large file):

To reproduce: run in `auto` mode and issue a Bash command the classifier blocks. The tool-substitution sentence is appended to the denial.

### Claude Model

Opus 5 (1M context) — `claude-opus-5[1m]`

### Is this a regression?

No. The behaviour is long-standing: the same tool-substitution sentence was reported in #74301 (filed before this report and closed as stale on 2026-09-05), so it predates the versions named here rather than appearing in one of them.

### Last Working Version

n/a — no version is known to have shipped the blocked-by-classifier denial without the tool-substitution sentence.

### Claude Code Version

2.1.263 — re-verified. The denial text above was first captured on 2.1.261 and reproduces **byte-for-byte identical** on 2.1.263, triggered by a Bash write to a path outside the session's working directory (`echo probe > ~/claude-denial-probe.txt`) in `permissions.defaultMode: auto`. The tool-substitution sentence is unchanged between the two versions.

### Platform

WSL2 (Ubuntu 24.04.3 LTS on Windows, kernel 6.6.87.2-microsoft-standard-WSL2)

### Operating System

Ubuntu 24.04.3 LTS

### Terminal/Shell

bash (`/bin/bash`) inside tmux, reached over ttyd in a browser

### Additional Information

**Configuration:** `permissions.defaultMode: auto`.

#### The other half is already fixed, which is why this is a small ask

In 2.1.261 the **conversation-scoped** refusal path (`stop_reason: refusal` from the separate safety check) no longer emits the coaching. It routes through its own message builder and says the opposite:

> Retrying it will hit the same refusal, so **don't rewrite or rework the action to get around this** — it reacts to earlier conversation content, not to the action itself, and it will keep firing for the rest of this conversation. Continue with other tasks that don't require this action. If it is essential, stop and tell the user that auto mode could not evaluate it, and suggest running this action outside auto mode (switch back to the default permission mode) or starting a fresh session.

The two paths are **not** the same control and I am not arguing they should carry the same text: a conversation-scoped refusal guards prohibited content, where reworking the request is always wrong, while the classifier is a scope/shape gate where a compliant alternative can legitimately be the right move. The narrow point is only that this path **selects its wording from the decision object** rather than appending one fixed paragraph to every block — so the plumbing for category-selected text already exists, and the ordinary "blocked by classifier" path just doesn't use it.

#### Why it matters beyond the security argument

When the classifier blocks a *goal* rather than a *command shape*, the coaching is followed and fails repeatedly — each reformulation is denied identically, burning turns — because the message asserted the action was the problem. The refusal path was fixed for precisely this reason; the same reasoning applies wherever the block is not about the literal command.

#### Not included here

The false-positive calibration complaint from #74301 is deliberately left out of this report — it is a separate issue and a judgment call. This report is only about the denial text.

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Línea de trabajo

Search for the exact tool-substitution sentence, then inspect the auto-mode classifier denial message builder and the decision object used by the refusal path. Trace how block categories are represented and make the wording category-specific: goal blocks should omit substitution coaching while shape-triggered blocks may retain it. Reproduce the reported auto-mode denial and verify the resulting message for both categories.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
bash, python
Área
authorization, cli, security
Tipo de issue
Error
Dificultad
3/5
Tiempo estimado
1-2 días
Estado de actividad
Activo
Claridad
Bastante claro
Aptitud para principiantes
62/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.