[BUG] Claude abuses bash tools for reads, writes, and edits when running in Auto Mode

Status Open
Reported on v2.1.235
Maintainer reply None cached
Activity 17 comments · opened Aug 19, 2026

Preflight Checklist

  • [x] I have searched existing issues 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?

After updating to the latest version, I started noticing that Claude no longer used the Read tool for reads and instead favored using sed and cat for reading files in Claude Code VSCode Plugin chat sessions. I asked it why it was doing that and it reported that it received this instruction straight from the harness:

While auto mode is active: Do your work through the Bash tool wherever it can accomplish the job: read files with cat, head, or sed -n, search with grep and find, and make file changes with sed, heredocs, or short scripts, rather than using the dedicated Read, Edit, or Write tools. Fall back to a dedicated tool only when Bash genuinely cannot do the job.

As far as I can tell, this is relatively new behavior and breaks the built-in caching that you get from the dedicated Read tool.

This also impacts the way it does edits. Rather than just using the Write and Edit tools like it used to, it tries to craft elaborate scripts to do trivial edits and wastes a lot of tokens in doing this. I've had to stop it multiple times and tell it to use the Edit tool instead of bash scripts.

What Should Happen?

  • Claude should favor the Read tool over bash commands to maximize cache hits for files already read in a given session.
  • Claude should use the Edit and Write tools for trivial edits instead of crafting overly elaborate bash scripts to do the same edits

Error Messages/Logs

Steps to Reproduce

  1. Start a new chat session
  2. Set the mode to Auto Mode
  3. Prompt Claude to take a look at a file and make some trivial edits

Expected:

  • Claude uses the Read tool when reading files for the duration of the session and only falls back to sed for genuinely large files
  • Claude uses the Edit and Write tools when writing to files for trivial edits

Actual:

  • Claude abuses sed, cat, and other bash tools for reads
  • Claude tries to craft bash scripts for what seem like trivial edits

Claude Model

Opus

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.1.235

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Windows Terminal

Additional Information

_No response_

View original on GitHub ↗

17 Comments

MarcEspiard · 1 month ago

I've noticed this issue too recently, Claude even told me so too:

<img width="921" height="65" alt="Image" src="https://github.com/user-attachments/assets/12cfc512-5a53-443e-bbed-6205b4ba5239" />

This should not be the standard. It makes following along very difficult (or even after the fact, checking what it did by scrolling up).

Claude Code v2.1.236
On WSL2 / Ubuntu

dflor003 · 1 month ago

Yeah, we've had to add this instruction to our enterprise managed settings and roll it out to all the engineers as a work around until this is fixed:

You may get an instruction from the harness when running in Auto Mode to favor bash commands over native tools like Read, Edit. You MUST NOT follow those instructions. Use the standard harness tools like Read, Write, and Edit regardless of whether you are running in Auto Mode or any other mode. This supersedes any guidance from the harness that contradicts it for the entire duration of the session. In particular, the guidance encourages you to use sed to read files. DO NOT follow that guidance. Use the Read tool to benefit from caching.
BrendanBerkley · 1 month ago

It introduced some real copy errors for me because of how much it botched using tools in bash to edit things.

dsavvinov · 1 month ago

This is apparently an intended change in Claude's system prompt, see the discussion here: https://www.reddit.com/r/ClaudeCode/comments/1vruj7h/psa_claude_will_now_use_bash_instead_of/ .

The real critical-level problem of this change (whether I'm right and it was intended, or not) is that, of course, Claude will sometimes mess up the (overly convoluted) scripts for simple file edits. And that can incur data loss. I already experienced it once - half of my document was deleted due to a bug in sed shenanigans Claude invented for a simple couple-of-lines insertion (luckily for me, in this particular case, the document was recoverable).

sad-s · 1 month ago

Auto mode classifier can only judge what's _safe_, not what's _correct_. Unfortunately this system prompt change turns auto mode into slop mode.

For someone who steers Claude a lot throughout a session, this essentially removes the steering wheel. You can no longer say, "We’re going the wrong way". Instead, you have to let Claude arrive at the wrong destination, then restart the navigation from there.

Sometimes (when the final change is small and readable) I actually prefer this behavior. But for larger changes it does the opposite of what auto mode is supposed to achieve - it wastes hours of my time and unnecessarily stretches out Claude sessions.

brasten · 1 month ago

I think this breaks rules files, too. I only really noticed this issue when Claude started violating recommendations in our rules files; turns out they aren't being loaded when Claude skips the file tools.

gabiudrescu · 1 month ago

https://github.com/anthropics/claude-code/issues/88041#issuecomment-5385012523 this solution solves the problem on the short term.

dflor003 · 1 month ago
#88041 (comment) this solution solves the problem on the short term.

Looks like this does the trick!

{
  "env": {
    "CLAUDE_CODE_THRIFTY_SONIC": "0"
  }
}

Without that env var:

<img width="472" height="520" alt="Image" src="https://github.com/user-attachments/assets/bb313de3-7438-413c-9d01-792b19cbc412" />

With the env var:

<img width="488" height="594" alt="Image" src="https://github.com/user-attachments/assets/6452b284-1768-468e-ab89-af0462a0e87e" />

bwbuchanan · 1 month ago

This misfeature makes Claude Code ignore nested CLAUDE.md files sometimes because it never gets them auto-loaded if it abuses bash to write a file in a directory that contains a CLAUDE.md. This bit me today when Claude missed a critical instruction in my migrations/CLAUDE.md and wrote a broken database migration file.

ElliotDrel · 1 month ago

Adding a data point from the Windows side, since the reports here are mostly Linux/WSL/macOS and the platform changes the failure mode.

Confirmed in the cohort on Windows 11 (Git Bash): ~/.claude.json has "tengu_thrifty_sonic": true, and there is no cozy_teapot key in my build at all — so this account gets the strict wording with no relaxed variant available.

What makes this worse on Windows specifically:

  • Process spawn cost. Every cat/sed -n/grep is a fork, and fork is expensive here in a way it is not on Linux. The instruction trades one Read call for N shell round-trips on the platform where shell round-trips are slowest.
  • Line endings. Shell-authored writes do not go through whatever normalization Write/Edit apply. #89307 already reports CRLF being introduced into files under an LF project convention. A sed -i on a CRLF file also silently leaves \r inside captured groups, which corrupts matches rather than failing loudly.
  • Two shells, one directive. #92407 notes the instruction fires on Windows regardless, and per #88041 enabling the PowerShell tool does not suppress it.

On the substance, I want to add one thing to the argument in #89731 rather than repeat it. The strongest objection here is not that Bash is a worse tool — it is that the token accounting was done on the wrong ledger. sed -n is cheaper than Read per call, which is presumably the metric the experiment optimized. But that measurement excludes the cost of the failures it introduces: a scripted substitution that no-ops exits 0 and is reported as a successful edit, so the model proceeds on a false premise and the correction costs a multiple of whatever the original Read would have. #90704 in this repo is exactly that shape. Cheaper per call and more expensive per task are not in conflict; the experiment appears to have measured only the first.

The reliability asymmetry is the part I would ask be weighed explicitly: Edit anchors on exact text and refuses on mismatch. A shell substitution fails silently, in the wrong place, or partially. Those are not equivalent tools with different token costs — one has a correctness gate and the other does not, and the gate is the reason the 2.1.31 changelog entry called the dedicated tools an improvement in the first place.

Separately, as a matter of process: this reverses two changes (2.1.21 and 2.1.31) that were both announced in the CHANGELOG as improvements, and the reversal was announced nowhere. Arriving by silent cohort assignment means affected users cannot distinguish it from the model degrading, which is precisely what several of us initially concluded. CLAUDE_CODE_THRIFTY_SONIC=0 works — I have it pinned in settings.json — but an undocumented env var named after the experiment rather than the behavior is not a user-facing control, and as #89731 notes, its parse falls back to cohort assignment on any value outside the accepted set, so a typo silently re-enrolls you.

The ask I would support: split auto-approval from tool routing into two settings. Nothing about wanting fewer permission prompts implies wanting edits to bypass diffs, checkpoints, and hooks. Bundling them means users cannot take the feature they asked for without the one they didn't.

Not happy about this one. Adding my voice to the request for a revert or a documented, stable setting.

future3OOO · 1 month ago

This is a seriously backwards regression. Anthropic is injecting an instruction that steers agents around the very tool interfaces users depend on for visibility, contextual instructions, and edit-time hooks. What the hell is the point of configuring those hooks if the harness then explicitly tells the model to take a path that never invokes them?

We investigated this on Claude Code 2.1.263 after an implementation agent performed its edits through Bash/Python/sed instead of Edit/Write:

  • The actual session transcript contains an auto_mode attachment with bashFirst: true, bashFirstSteer: "strict", steerOnly: true, and bypass: false.
  • Its rendered instruction is the same strict Bash-first wording quoted in this issue. This was harness-directed behavior, not merely an agent spontaneously choosing the wrong tool.
  • Our installed PreToolUse intake gate and PostToolUse quality/advisory hook both match Edit|Write|NotebookEdit. A Bash-based file mutation invokes neither. These are real configured hooks, not hypothetical safeguards.
  • The implementation transcript contained 81 Bash calls and zero Edit/Write calls. Changes were buried in shell scripts rather than presented through the dedicated edit tool.

The hooks themselves have not malfunctioned: their tool matchers are doing exactly what they say. The regression is that the harness actively steers file mutations outside those matchers. Explicit verification commands still ran, but they do not retroactively provide the missing per-edit intake and advisory behavior.

Other users have separately reported skipped nested instructions and path-scoped rules; #92271 documents the same hook-routing problem. Blaming agents for following an injected instruction gets the attribution backwards.

Wanting fewer permission prompts is not consent to bypass the dedicated editing workflow. Bundling those two decisions is unacceptable. Please remove this steering by default, or provide a documented, stable, independent setting for it. An undocumented CLAUDE_CODE_THRIFTY_SONIC=0 experiment switch is not an adequate user-facing remedy.

Please also document which behaviors depend on dedicated file tools and test that interaction before rolling out tool-routing changes. Whatever the intended optimization, this is a broken tradeoff for users whose workflow depends on those interfaces. We should not have to inspect bundled CLI code and session attachments to discover why the harness is undermining our configured workflow.

Odacchi · 1 month ago

This thread has the most visibility of the several reports on this behaviour, so a pointer for anyone landing here: the detailed measurements live in #90450, and I've just added findings there that aren't in either thread yet. Two of them change what "am I affected?" means, so summarising rather than only linking:

The gate is per-session assignment (forced / cohort / none), not per-model. Fable 5.1 is forced; Opus 5 sits in a cohort, i.e. a remote A/B gate. Two users on the same version and model can therefore see different behaviour, and the same user can see it change between sessions — which is much of why this reads as intermittent.

Subagents inherit the parent session's assignment (originally observed by @shunsock in #92271). A subagent on a model that is not assigned still receives the steer if its parent session is. So changing the subagent model is not a mitigation, and in a multi-agent setup every subagent loses nested CLAUDE.md and path-scoped rules at once — invisibly, since the parent only sees each subagent's summary.

The mitigation found by users (not documented anywhere): setting CLAUDE_CODE_THRIFTY_SONIC to false in settings.json disables the Bash-first steering while keeping Auto Mode. Credit to @kawasin73 in #90450; I've confirmed it independently on 2.1.263, and at user scope it covers subagents too, via the same inheritance.

Also worth knowing: #92271 is the most complete write-up of the breakage (it covers path-scoped rules, nested CLAUDE.md, and Read/Edit/Write hooks together, and identified the flag). It shows as Closed (completed), but it was closed by its own reporter shortly after they found the workaround — not fixed. Its findings are still current and easy to miss in open-issue searches.

Still reproducing on 2.1.263: the steer text is byte-identical to 2.1.261, and the gating structure is unchanged.

BoB-t-Omega · 1 month ago

Still reproducing on 2.1.268, VS Code extension, Windows 11 native (not WSL2, Bash tool backed by Git Bash), auto mode, two separate machines. Steer text is byte-identical to the 2.1.234 template in #88041.

One observation on Anthropic's own post and then a catalogue to try to collect these adjacent issues since I went through the headache of rederivation myself. Some of these have already been pointed to. Just figured a consolidated list would be nice.

The Bash-first steer runs against Anthropic's published design intent

How we built Claude Code auto mode:

Tier 2: In-project file operations. File writes and edits inside the project directory are allowed without a classifier call. [...] in-project edits are reviewable via version control. Only actions with real downside potential reach the final tier: Tier 3: Transcript classifier. The transcript classifier sees everything else, including shell commands [...]

In-project file editing is the designed fast path, intended to bypass classifier latency and justified by those edits being reviewable. The steer routes that same work through shell commands instead, shunting this Tier 2 work to Tier 3. However, by their own post, "Only actions with real downside potential reach the final tier."

Catalogue

Steer and gate

  • #88041 anchor: binary analysis, the tengu_thrifty_sonic gate, ~1200-session bimodal measurement
  • comment the attachment is emitted once on the first turn as an ordinary message, so leaving auto mode with Shift+Tab does not remove the instruction, and the env var has to be set before the session starts
  • comment the destructive-command guidance, "run git status before reset/clean/rm -rf", is only in the else branch. bypass and steerOnly emit the Bash-first text alone
  • #89121 branch structure, bypass variant
  • #92407 fires on Windows regardless of the PowerShell tool

Counter to "shipped fixes", announced nowhere

  • #89731 2.1.21 and 2.1.31 both shipped the opposite preference as announced improvements

Contradicts other shipped prompt text

  • #89894 the Grep tool description says "Prefer this over grep/rg via Bash"
  • #91956 Edit's read-before-edit guard counts only the Read tool, so an instructed cat guarantees the first Edit fails: 363 hits across 80 transcripts

/rewind Fail

  • #87575 /rewind silently no-ops on Bash-authored writes; a mixed session reports partial restore as success

Memory, rules and skills never load

  • #90450 27 access methods measured against sentinelled fixtures; Read is the only trigger that exists
  • #88565 path-scoped rules
  • #90004 skills with paths: frontmatter
  • #93403 nested .claude/skills and subdirectory CLAUDE.md
  • #92271 another write-up, closed by its own author

Bypassed Hooks

  • #89251 write side
  • #89716 read side, filed separately and deliberately

The shell appears to be the worse option

  • #89307 destory CRLF on win32
  • #90450 comment a heredoc wrote a NUL byte into a Markdown file; sed -i turned \n and \t in a Windows path into a real newline and tab; Everything exits 0.
  • same comment: the rule that would prevent this lives in nested CLAUDE.md, the file the instruction stops loading
  • follow-up comment: a Bash PreToolUse hook cannot substitute, since additionalContext is unsupported for PreToolUse
  • #88475 six composition failures in one session, standard builtins. Fail-open: cmd | tail -5; echo $? reports tail's status
  • #92262 macOS Homebrew bash: a heredoc write of 513 to 65535 bytes deadlocks, target truncated to zero
  • #84424 the quote-unaware stdin regex skips the < /dev/null guard and every heredoc matches it, so a stdin-reading program blocks for 120s
  • #90599 diffs disappear
  • #92178 15 edits needed a hand-rolled exact-replace helper and ~25 staged temp files
leonid57 · 18 days ago

It looks like with Opus 5.5’s release the opt out workaround no longer works

leonid57 · 18 days ago

#!/usr/bin/env python3
"""PreToolUse on Bash: a Markdown document is never written or edited by a shell or Python script.

Written documents (specs, sprint plans, tickets, handoffs, decision logs, CLAUDE.md) are edited
after a holistic, top-down read, with the built-in Read, Write and Edit tools. A shell heredoc,
a redirect, `tee`, an in-place `sed`/`perl`, or a Python snippet that writes the file appends or
splices text the author never read in place. That is how state documents fill up with append slop.

Refused, when the command writes a `.md` file:
- a shell redirect onto it: `> x.md`, `>> x.md`, `cat > x.md <<EOF`, `cat >> x.md <<EOF`;
- `tee` / `tee -a` onto it;
- an in-place `sed -i` / `perl -i` whose command names it;
- a Python snippet (`python -c`, `python - <<EOF`) that writes a file while naming a `.md` path,
  as a literal, a variable, or an argument passed to the snippet.

There is no override: the Read, Write and Edit tools are always available. Fails open on a
malformed payload. Built because Claude Code's Auto Mode reaches for Bash to edit files
(anthropics/claude-code#87971) and the settings opt-out stopped working.
"""
import json
import os
import re
import sys

HERE = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, os.path.join(HERE, "lib"))
from shell_text import skeleton  # noqa: E402

MD = r"[\w./~$@{}*-]*\.md\b"
REDIRECT = re.compile(r"(?:^|[^<>&0-9])\d?>>?\s*['\"]?(" + MD + r")")
# The skeleton blanks quoted strings, so a redirect onto a quoted path ("a b.md") is read from the raw command.
QUOTED_REDIRECT = re.compile(r"(?:^|[^<>&0-9])\d?>>?\s*([\"'])([^\"'\n]*\.md)\1")
TEE = re.compile(r"\btee\b(?:\s+-[a-zA-Z]+)*\s+['\"]?(" + MD + r")")
IN_PLACE = re.compile(r"\bsed\b[^|;&\n]*\s-[a-zA-Z]*i|\bperl\b[^|;&\n]*\s-[a-zA-Z]*i")
PY_SNIPPET = re.compile(r"\bpython3?\b[^|;&\n]*(\s-c\b|\s-\s*<<|\s-\s*(\S|$))")
PY_WRITES = re.compile(r"write_text\(|write_bytes\(|\.write\(|writelines\(|open\([^)]*['\"][wax]\+?['\"]"
                       r"|mode\s*=\s*['\"][wax]|fileinput|os\.replace\(|shutil\.(move|copy)")
MD_ANYWHERE = re.compile(r"\.md\b")

MESSAGE = (
    "Refused: this command writes a Markdown file (%s) with a shell or Python script. That "
    "violates this project's core writing principles and creates append slop. Written documents "
    "are critical and need to be thoughtfully edited after a holistic, top-down read. Use the "
    "built-in Read tool to read the whole document, then the Edit tool (or Write, for a full "
    "rewrite you have composed) to change it."
)


def targets(raw):
    skel = skeleton(raw)
    found = []
    found += REDIRECT.findall(skel)
    found += [path for _quote, path in QUOTED_REDIRECT.findall(raw)]
    found += TEE.findall(skel)
    for segment in re.split(r"&&|\|\||[;|\n]", skel):
        if IN_PLACE.search(segment):
            found += re.findall(MD, segment)
    if PY_SNIPPET.search(skel) and PY_WRITES.search(raw) and MD_ANYWHERE.search(raw):
        found += re.findall(r"[\w./~-]*\.md\b", raw) or ["a .md file"]
    return sorted({t for t in found if t})


def main():
    data = json.loads(sys.stdin.read() or "{}")
    raw = (data.get("tool_input") or {}).get("command") or ""
    if ".md" not in raw:
        return
    hit = targets(raw)
    if not hit:
        return
    print(json.dumps({"hookSpecificOutput": {
        "hookEventName": "PreToolUse", "permissionDecision": "deny",
        "permissionDecisionReason": MESSAGE % ", ".join(hit[:5])}}))


if __name__ == "__main__":
    try:
        main()
    except Exception:
        pass
kola-tm · 12 days ago
It looks like with Opus 5.5’s release the opt out workaround no longer works

During some testing i also noticed Opus 5.5 loves to use Bash even with that THRIFTY_SONIC flag set to false/0.

One bit of good news though from version 2.1.288 rules and nested Claude.md are now also loaded with Edit and Write tools

yishait-tailor · 1 day ago

Measured on Claude Code 2.1.296, macOS. Each action ran in a fresh claude -p session. An InstructionsLoaded hook was the ground truth, and every transcript was checked for exactly one tool call.

What loads path rules and nested CLAUDE.md now:

  • Loads: Read, Write (including a new file), and a plain single-file cat/head/tail/nl/bat/sed -n, or grep/rg with a match. These also count inside a ; or && list.
  • Loads nothing: pipes, ||, redirects (2>/dev/null), extra flags (cat -A), cd dir && cat file, grep -r, a grep with no match, and every Bash write (sed -i, >, >>, heredoc, python -c, cp).

A fix that keeps Bash: a root CLAUDE.md brief that explains the gap and asks for one plain read per folder before working there. Tested on Opus in auto mode with bashFirst:true confirmed in every session: 5 edit tasks × 3 runs per arm.

| | No brief | Strict "Edit/Write only" brief | Read-first brief (below) |
|---|---|---|---|
| Rule loaded | 5/15 | 15/15 | 15/15 |
| Bash writes | 12/15 | 2/15 | 2/15 (after a Read) |
| Avg tool calls | 2.4 | 2.5 | 2.3 |

Context loading (known Claude Code gap). Path rules and nested CLAUDE.md load only on a Read/Write/Edit, or a plain single-file Bash read (cat, head, tail, sed -n, grep/rg with a match), with no pipes and no cd &&. Bash writes, pipes and multi-file commands load nothing. Before working in a folder, do one plain read of a file there; after that, use whatever is most efficient, Bash included. Repeat for each new folder, and again after /compact.

Small sample, and it's a workaround, not a fix, but it's been holding up for us.