[BUG] Claude generates tight gh run view polling loops that exhaust GitHub API rate limits
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?
Category: Model behavior (not CLI tooling)
When asked to wait for a GitHub Actions CI run to complete, Claude generates shell polling loops like
this:
until gh run view <run-id> 2>&1 | grep -q "completed"; do true; done
This pattern has two critical flaws:
- No delay between iterations — do true; done runs as fast as the shell allows, calling gh run view
hundreds of times per minute. This exhausts the GitHub REST API rate limit (5,000 requests/hour) in
under an hour.
- No awareness of terminal states — the loop has no logic to exit if the run fails, is cancelled, or
errors out. It can run indefinitely even when the workflow is long gone.
Real-world impact
In our session, Claude spawned multiple of these loops in the background across different CI runs. They
continued running silently in the background after the session moved on. By the time we noticed, the
GitHub API rate limit was completely exhausted (0/5000), blocking all gh CLI commands for the remainder
of the hour.
Root cause
This is a model training issue, not a CLI bug. Claude is reaching for a generic shell pattern instead of
the purpose-built command that already exists:
gh run watch <run-id> --exit-status
gh run watch streams live status, exits automatically when the run completes/fails/is cancelled, and
makes efficient use of the API — no polling loop needed.
Expected behavior
Claude should always use gh run watch <run-id> when waiting for a CI run. It should never generate until
... do true; done or any manual polling loop against the GitHub API.
Suggested fix
Add this to model training or system prompt guardrails:
- When waiting for a GitHub Actions run: use gh run watch, never a manual loop
- Any shell loop that calls an external API must have a sleep — but even that is a fallback, not the
right answer here
What Should Happen?
Claude should always use gh run watch <run-id> when waiting for a CI run. It should never generate until
... do true; done or any manual polling loop against the GitHub API.
Error Messages/Logs
Steps to Reproduce
Steps to Reproduce
- Open Claude Code CLI (claude command)
- Have an active GitHub Actions workflow running (e.g. push to a branch with CI configured)
- Ask Claude something like:
- "Wait for the CI run to complete before merging"
- "Check if the CI passed and then create a PR"
- "Monitor the GitHub Actions run and tell me when it's done"
- Claude generates and executes a shell command like:
until gh run view <run-id> 2>&1 | grep -q "completed"; do true; done
- Observe in ps aux that the gh run view process is being spawned continuously with no delay
- Check GitHub API rate limit:
gh api rate_limit | python3 -c "import json,sys; r=json.load(sys.stdin)['resources']['core'];
print(f'{r[\"remaining\"]}/{r[\"limit\"]}')"
- Rate limit drops to 0/5000 within minutes
Additional observation: If Claude runs multiple such loops in parallel (e.g. watching two CI runs), the
rate limit is exhausted even faster. The loops also continue running silently in the background after
Claude moves on to other tasks — they are not cleaned up.
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.141
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
- The loops were spawned as background processes — Claude ran them with run_in_background via the Bash
tool and never terminated them, even after moving on to unrelated tasks in the same session
- Multiple loops ran in parallel against different run IDs, compounding the API consumption
- The issue was only discovered when gh pr create failed with API rate limit already exceeded — there
was no warning or self-correction from Claude during the session
- gh run watch is already documented in the gh CLI and is the idiomatic solution — this is purely a
matter of Claude not choosing the right tool
- Workaround: Run pkill -f "gh run view" to kill any runaway loops if you hit this issue
gh CLI version: 2.80.0
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗