Background/auto sessions busy-wait-spam instead of idling on Monitor/background-task notifications
Background/auto sessions busy-wait-spam instead of idling on Monitor/background-task notifications
In a background, auto-permission session (sessionKind: "bg", permissionMode: "auto"), starting a long-running async wait — via the Monitor tool or Bash with run_in_background: true — can trigger a runaway busy-wait loop instead of the model idling until the promised notification arrives.
Observed behavior
After starting the async wait, the tool result correctly instructs the model: "You will be notified on each event. Keep working — do not poll or sleep." But the very next injected turnReminder (from the active "Proactive" output style) says "Execute autonomously, minimize interruptions, prefer action over planning." With no other work queued, the model resolves this conflict by manufacturing a throwaway action each step: a one-line filler message ("Waiting.", "I'll wait for the monitor.") followed by a manual Read/tail of the task's log or .output file — repeated every 2-5 seconds, dozens of times (observed up to ~90), all within a single turn (same promptId), until interrupted by the user or the run ends.
Confirmed twice in one session, via two different trigger paths:
- Started via
Monitor(until grep -q 'Test Summary|ERROR' .../full.log ...), then manually re-Readthe same log ~85 times, 2-5s apart. - Started via
Bash ... run_in_background: true(noMonitorcall at all), then manually re-Readthe.outputfile 5 times, each returning "file exists but empty."
Steps to reproduce
- Run a background/auto-permission Claude Code session (
--permission-mode autoor equivalent bg session) with the "Proactive" output style active. - Give the agent a task that ends in kicking off a long-running shell command with no further work queued after it — e.g. a slow test suite — via either:
- the
Monitortool (until <condition>; do sleep N; done; tail ...), or Bashwithrun_in_background: true.
- Ensure the command genuinely takes a while (tens of seconds to minutes) and that the agent has nothing else left on its plate.
- Watch the transcript: instead of ending its turn / going idle until the async notification fires, the agent repeatedly emits a short filler line ("Waiting.", "I'll wait for the monitor.") followed by a manual
Read/tailof the task's output file, in a tight loop, all under the samepromptId. - This continues until manually interrupted, context budget is exhausted, or (rarely) the awaited condition resolves.
Detection (for anyone hitting this)
In the session's .jsonl transcript, look for 3+ short assistant text blocks (<40 chars) sharing one promptId in a tight time window — that's the signature of this loop.
Impact
Session transcripts balloon with near-duplicate filler messages, burning tokens/time, and (in interactive use) it's confusing/alarming to see the same message repeated ~100 times.
Suggested fix
When a Monitor/background-task notification is pending and no other actionable work exists, either suppress the "prefer action over planning" turn reminder, or have the harness itself hold the turn rather than re-prompting the model to act.
Environment
Claude Code v2.1.241, model claude-opus-5.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗