[Bug] Agent repeatedly calls Bash(true) instead of awaiting response

Status Open
Reported on v2.1.202
Maintainer reply None cached
Activity 3 comments · opened Jul 15, 2026

Bug Description
Kept calling Bash(true) and spinning and burning tokens instead of just waiting for an agent to return.

Environment Info

  • Platform: darwin
  • Terminal: iTerm.app
  • Version: 2.1.202
  • Feedback ID: e9a7f6da-ff1c-4799-858a-4636b8d72843

Errors

[]

View original on GitHub ↗

3 Comments

timhorn1 · 1 month ago

I also do have this Bug running claude code on archlinux. Telling the agent not to do that did result less into this bug, but still sometimes

technicalpickles · 1 month ago

Dug into this on my own session logs and can add some data.

It's not tied to one release: I found the same idiom (bare Bash(true), no args, no chaining) used as filler while waiting on a background subagent/fork, across CC 2.1.220, 2.1.226 (two separate sessions), 2.1.227, and 2.1.239, spread over about a month. 42 occurrences total out of ~27k Bash calls in my logs, so it's rare but persistent.

The clearest case (2.1.239, permissionMode: "auto"): a general-purpose subagent had dispatched 5 of its own background forks to survey a codebase in parallel. Partway through, its own reasoning says, verbatim: "I'll just wait for the remaining task notifications instead of polling." So it correctly picked up on the "don't poll" instruction. But instead of actually going quiet, it kept making a Bash call every turn whenever nothing else was queued, and that call was always bare true. 35 of those in that one subagent run, tapering off once the last forks' results landed.

I checked the actual system prompt for that exact build (Piebald-AI/claude-code-system-prompts, tag v2.1.239) to see if true specifically is prescribed anywhere. It isn't. The Agent tool's usage notes say "do NOT sleep, poll, or proactively check on its progress. Continue with other work or respond to the user instead" — nothing about a placeholder command. So the "don't poll" instinct is correctly system-prompt-driven, but the model still seems to feel it needs some tool call each turn, and grabs the most inert one available rather than actually ending its turn without one.

Happy to share the exact queries/timestamps if it's useful for reproducing.

Khakimovpro · 1 month ago

Still reproducing on 2.1.257 — the build whose changelog entry says the Proactive-output-style busy-loop was fixed. No Proactive style is involved in my case, so this looks like the second path @technicalpickles described above rather than a regression of that fix.

Environment: Claude Code 2.1.257, VS Code extension (entrypoint: claude-vscode), Linux, Opus 5 (1M context), permissionMode: bypassPermissions.

Measured from my own transcripts (~/.claude/projects/**/*.jsonl, parsed, not eyeballed):

| session | date | no-op form | count | wall time |
|---|---|---|---|---|
| A | 2026-09-09 | true | 681 | 18 min |
| B | 2026-09-08 | echo . | 683 | 34 min |

In session A the model also emitted a one-word text message before most calls — the Russian for "waiting" ×135, plus three longer variants ×164. Across one month of logs: 8,137 idle turns (3.7% of all assistant turns), ~5 hours of wall clock, and 3.19B cache-read tokens re-processed for zero output.

Trigger is consistent: the loop starts immediately after handing work off — a background bash watcher, a detached setsid nohup run, or background subagents — while waiting for a gate/deploy/subagent. 95% of the no-op calls occur within 60 events of such a handoff. Task notifications were arriving normally in those very sessions (1,568 of them across the logs), so the wake-up mechanism worked fine. The model simply never treated "end the turn" as a way to wait.

The spelling keeps mutating: echo idle → echo ok → echo . → true over three weeks. Denying one spelling just renames the symptom.

A data point on whether guidance can fix this — it cannot. My setup carries an explicit, emphatic written rule against posting "waiting" chatter. The rule did not stop the loop; it changed its shape. Before it, idle turns were text ("waiting…"). After it, they became silent no-op tool calls, which are strictly worse: identical context re-processing, no visible reason, and the user sees a wall of empty Bash calls instead of a wall of words. This matches the reasoning trace quoted earlier in this thread — the model states it will wait for notifications, then calls true anyway.

Workaround that holds, for anyone landing here: a PreToolUse hook on Bash that denies bare true / : / echo . / sleep N and returns the instruction in permissionDecisionReason ("start one background command and end your turn — the harness will wake you"). It takes effect without restarting the session, and the denial text is what actually redirects the model. Two caveats learned the hard way: make the hook fail-open, and match the command as a whole rather than line-by-line, or a multi-line script containing a bare echo gets blocked.

That is symptom treatment though. The missing piece is still the one named in #32085: no primitive, and no documented statement, that ending a turn is a valid way to wait.