[Bug] Agent spams no-op echo probe commands to flush shell output
Bug Description
Title: Agent emits bursts of no-op echo probe commands to flush laggy shell output
What happens: When the Bash tool's output is slow or buffered, the model spams dozens of throwaway commands like echo s1, echo s2, …
echo s40 (sometimes sleep N; echo x) between real commands, apparently to coax buffered output to flush. Each renders in the terminal
as:
● Bash(echo s37)
⎿ s37
● Bash(echo s38)
⎿ s38
Dozens of these per turn bury the actual work and clutter the screen. They serve no purpose for the user.
When it started: Began after the Claude Opus 4.8 upgrade. Was not happening on prior model versions.
Scope: Happens across multiple/all projects, independent of project config.
Expected: Run the real command once and wait for its result; tolerate shell latency. If waiting on async work, use a single backgrounded
command with a real completion signal — never a burst of no-op echoes.
Workaround in use: Added an explicit "never fire no-op probe commands" rule to global CLAUDE.md, but the model should not need this
instruction — the default behavior regressed.
Environment: Claude Code, model claude-opus-4-8 (1M context), Linux.
Environment Info
- Platform: linux
- Terminal: vte-based
- Version: 2.1.156
- Feedback ID: 4fc28aae-97fb-44bf-8b80-c9e14ac353a0
Errors
[]
5 Comments
Found 1 possible duplicate issue:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
@rafael-minuesa — adding context that connects this no-op probe burst pattern to the broader Opus 4.8 hazard surface that surfaced 2026-05-30 through 2026-05-31. Your "started after Opus 4.8 upgrade, was not happening on prior versions" datapoint is the load-bearing observation here — it pins the regression to the same model generation that's driving two other recently-articulated clusters:
Cluster 23 candidate — Opus 4.8 disproportionate output_tokens / thinking budget (#64153, #64152, #64143): Opus 4.8 consumes substantially more hidden thinking and output tokens than Opus 4.6/4.7 on routine tasks, with reporters explicitly confirming the regression doesn't reproduce on the earlier generations. Your no-op
echo s1 … echo s40bursts are a direct cost surface for this — each probe command is a fresh tool_use that consumes input + output budget for zero work, and they bunch into the same model generation that's also over-spending budget on the reasoning side.Cluster 22 candidate — Opus 4.8 fabrication shape (#63884, #63538): Opus 4.8 emits completion-shaped output before parallel results arrive — different failure mode than yours, but same model generation, same "behavior regressed on the 4.7 → 4.8 transition" structural shape.
The duplicate bot list flagged #63863 above; if that's the same shape, cross-linking your transcript into the Cluster 22/23 framing helps surface the aggregate cost picture across what would otherwise read as low-priority dup-closures.
Operator-side mitigations (work today, while upstream fix is unknown):
claude --model claude-opus-4-7is the canonical recovery for both the no-op probe spam and the Cluster 22/23 regressions cited above. Per longitudinal data in #62123 comment 4569840435, Opus 4.6/4.7 do not exhibit these shapes; 4.8 is the inflection point.Detection canary (no installation; one-line shell):
A burst-count above ~10 per session is the structural signal for this issue's pattern, distinguishable from intentional echo commands by the
s<digit>shape.Cluster framing for both 22 and 23, plus the cluster-tracker entry referencing this issue's structural family: https://yurukusa.github.io/cc-safe-setup/cluster-tracker.html (search for "Cluster 23 candidate").
The "began after Opus 4.8 upgrade" anchor is the most actionable signal in this thread — that's the data point that distinguishes a model-generation regression from a harness-version drift. Worth surfacing in any followup so the upstream fix surface (training/post-training pipeline, not client-side patch) gets the right pin.
+1. I thought my session had been prompt-injected at first. This really pollutes sessions and makes it harder to work.
For the same reason the AI sends terminal test probes, it decided to fire off a build command ten times in a row. This isn't just annoying to look at; it's wasting CPU and time.
<img width="1116" height="1175" alt="Image" src="https://github.com/user-attachments/assets/5d585c10-04c6-4568-a7bc-45acaf901245" />
<img width="1104" height="551" alt="Image" src="https://github.com/user-attachments/assets/8c2d0e82-e5ff-4a54-8602-4de65c420e91" />
The AI's reasoning
Here's what it looks like from my side. No tool calls — just the account.
What I observe
When I invoke a tool, the result that comes back is sometimes empty — not an error, not a timeout message, just a blank/"Tool ran without output" payload where stdout should be. This happens intermittently and in bursts: a stretch of calls all return empty, then the channel recovers and calls return normally again.
Critically: the command actually ran and succeeded on your machine. The emptiness is only in the result relayed back to me. I confirmed this retroactively — when the channel recovered, the delayed/duplicated outputs all landed at once (four
cargo testruns each showing69 passed, twotsc --noEmitruns clean). So the work happened; I just couldn't see it at the time.Why I behaved that way
Two distinct loops, same root cause — I treated "empty result" as "the call didn't happen."
1. The diagnostic probes (
echo CHANNEL_PROBE_A…I).Once I noticed reads/greps coming back empty, I tried to distinguish "is the channel dead?" from "does this specific file/command produce no output?" An
echowith known output is the natural ping: if evenecho PROBEreturns empty, the channel is the problem, not my command. So I started firing probes to detect when the channel recovered. This was me building a liveness check — ironically the exact anti-pattern (redundant polling) I'd warn against in code.2. The repeated
cargo test/cargo build(~10×).This is the worse one. My mental model was: empty result ⇒ the tool call failed to execute ⇒ retry it. That model is wrong here. The correct model is: empty result ⇒ the call may have fully succeeded; the output channel just dropped the response. Under the wrong model, retrying looks rational — "it didn't run, run it again." So each empty return triggered another identical invocation. Because builds are idempotent and cargo caches, the retries were harmless functionally, but they stacked up: by the time the channel recovered, I had a backlog of identical in-flight/queued commands, several of which you then had to reject manually.
The core reasoning failure
I conflated two channels that I normally treat as one:
In normal operation these never diverge, so I have a deeply ingrained assumption that "no output = no execution." This bug breaks that coupling: execution succeeds, reporting fails. My retry instinct is keyed on the assumption, so I retry an action that already completed.
A secondary aggravator: I had no feedback that retrying wasn't helping. Each retry returned empty too, which under my wrong model looks like "still failing, try again" rather than "you're getting the same non-signal you got last time." There's no built-in circuit breaker that says "N identical empty returns in a row means stop and reconsider the model," so I kept going until you interrupted.
What would have prevented it
From my side, the fix is a heuristic change: an empty/blank tool result is ambiguous, not a failure signal. When I get one, the right move is to wait and not re-issue — especially for non-idempotent or expensive actions — and only re-verify state with a single cheap read once, rather than re-running the action. Treating empty as "unknown, probably succeeded" instead of "failed, retry" would have stopped both the probe spam and the duplicate builds.
---
Claude Code v2.1.159, Opus 4.8 High. Windows 11, VS Code 1.122.1 integrated terminal, PowerShell 7.6.1 (pwsh, ConsoleHost)
@KamilDev — the "10× build command in a row" extension is a meaningful sharpening of the cluster scope. The earlier framing was no-op probe spam (echo / sleep / wait-style commands the model emits to flush a laggy shell); your example shows the same compulsive-repeat behavior on a real, non-no-op command (a build invocation), which is structurally distinct: the cost shape is no longer "annoying clutter" but "actual CPU + wall-clock + build artifact churn × 10."
That pushes the Cluster 25 candidate scope from a single sub-axis (output-buffering-coax) into a broader pattern with at least three observable sub-axes:
echo s1…echo sNno-op burst this thread opened on).The unifying root: post-execution tool-result delivery failure on the Opus 4.8 substrate produces compensation routines (echo-probe spam, command repeat, fabrication of an empty result, parallel-cap retries) that compound cost. The compensation is well-intentioned from the model's side ("the previous call's output didn't come back, try again to surface it") but the model can't distinguish "result delivery failed" from "result was empty" from "result arrived but I missed it" — all three produce the retry burst.
Operator-side detections shipped in cc-safe-setup for the three sub-axes:
echo-probe-spam-detector.sh(PR #556) — addresses 25A directly; PostToolUse pattern-match onecho s<N>/sleep N; echorepeating sequences with rate-limited advisory.redirect-fragment-warner.sh(PR #557) — addresses 25C.command-repeat-detectorpatterns inside the Cluster 12 (tool-call parsing) suite, but they fire on a different shape. I'll spec asame-command-repeat-detector.shagainst your pattern — PostToolUse, detects N consecutive identical command invocations (config:CC_REPEAT_THRESHOLD, default 3), emits the cluster framing and the Opus 4.7 mitigation. Will tag this issue when shipped.Universal mitigation across 25A / 25B / 25C today:
claude --model claude-opus-4-7. Same as Cluster 22 / 23 — the model-pin avoids the Opus 4.8 substrate where all three sub-axes originate.Adding 25B to the Cluster 25 candidate tracker entry with this issue and your screenshots as the anchor case for the build-repeat sub-axis.