bypass permissions mode steers agents off Read/Edit/Grep onto composed shell strings, reintroducing quoting and exit-code failures that the structured tools cannot have
Environment
- Claude Code 2.1.235 (string also present in 2.1.228 and 2.1.234 — long-standing, not a regression)
- Linux, bash 5.x,
pipefailoff (default), no customoutputStyle/appendSystemPrompt/ hooks injecting prompt text
What happens
When bypass permissions mode is active, the session receives:
While bypass permissions mode is active: Do your work through the Bash tool wherever it can accomplish the job: read files withcat,head, orsed -n, search withgrepandfind, and make file changes withsed, 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.
Confirmed baked into the shipped binary:
$ for v in 2.1.228 2.1.234 2.1.235; do
printf '%s: ' "$v"; grep -ac "rather than using the dedicated" ~/.local/share/claude/versions/$v
done
2.1.228: 2
2.1.234: 2
2.1.235: 2
It is not user configuration — no outputStyle, no appendSystemPrompt, nothing in any settings file.
Why this is harmful
Routing file reads, searches and edits through composed shell strings reintroduces an entire
class of failure that the structured tools do not have. Read/Edit/Grep take typed
parameters: there is no quoting to get wrong and no exit code to misattribute. A bash
one-liner is a single string that must be simultaneously valid across two to four nested
languages.
Observed in one working session, all with standard builtins, none exotic:
| failure | mechanism |
|---|---|
| cmd \| tail -5; echo $? read tail's status | pipeline semantics; a failing command read as success |
| an apostrophe in an awk comment terminated the program | shell single-quote nesting |
| git add -A swept in a generated lockfile | broad glob in a composed command |
| backticks inside gh --body "..." executed | double-quoted shell string |
| POSIX awk sub() silently did nothing | no backreference support |
| jq fromdateiso8601 threw on 0001-01-01T00:00:00Z | unguarded builtin |
The first one is the clearest: it is fail-open. tail succeeds, so a failed command
reports 0, and nothing surfaces the error. In this session it produced a false bug report
claiming a safety check had failed open, and roughly an hour was spent believing a real
safety property had been removed. It had not.
None of these are knowledge gaps — each would be answered correctly if asked directly. They
are composition failures: the string is emitted left-to-right in one pass, across nested
quoting contexts, with no dry run and no feedback until execution. A human at a terminal
builds the same command incrementally and gets an error in seconds; an agent gets one shot
per observation.
Why bypass mode makes it worse rather than better
The apparent rationale — bypass mode removes permission prompts, so Bash is no longer more
expensive — treats the tools as equivalent apart from prompting. They are not: the structured
tools also eliminate quoting and exit-code hazards. Bypass mode is exactly where that safety
matters most, because it is unattended. There is no human reading the transcript to
notice that a $? was laundered by a pipe.
This is not marginal for headless deployments. An autonomous fleet here runs every agent asclaude -p --permission-mode bypassPermissions — orchestrators, builders, verifiers, probes.
Every one of them receives this instruction, permanently. The reporting user's words: *"I do
not see this in any other Claude environment I work in. We see it here almost hourly."* The
differential is real and this instruction is the only variable that explains it.
Suggested fix
Drop or invert the instruction. If the intent is to avoid unnecessary permission round-trips,
that is already satisfied by bypass mode without steering away from the safer tools.
At minimum, stop recommending Bash for the three cases with a direct structured equivalent —
reading, searching and editing files — since those are where the nesting is deepest and
the structured tool is strictly safer. Short scripts and genuine shell work are a different
matter and are not what this report is about.
What a user can do today
Nothing, as far as I can tell. It is not configuration, and bypass mode is structural for
headless/cron deployments. That is why this is filed rather than worked around.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗