Claude Code is running pnpm test for the eighth time. Each iteration: edit the same expect(...).toBe(...) line, run again, fail again, revert. Or it’s ping-ponging between two interpretations of “fix the auth bug” — fixing OAuth, breaking magic-link, fixing magic-link, breaking OAuth. Tokens burn, the progress bar doesn’t move. The agent is in a loop.
Most loops aren’t “the model can’t fix this.” They’re a broken feedback loop: a flaky test, an under-constrained prompt, polluted context, or an unflagged tool failure. The fix isn’t a smarter model — it’s identifying the loop signature in 30 seconds and breaking it with one targeted prompt or one keystroke. Below are the six loop shapes, the constraint that breaks each, and the exact Claude Code commands (Ctrl+C, double-Esc / /rewind, /clear, /compact) you need to recover. Verified against Claude Code as of June 2026, running Claude Opus 4.7 / Sonnet 4.6.
TL;DR — break a loop in 60 seconds
- Interrupt now. Press
Escto stop the current action;Ctrl+Cto cancel a hung command. Don’t wait for the loop to “finish” — it won’t. - Read the trace. Ask Claude to list its last 10 actions as a table (file + edit + command + exit code). The pattern is almost always two files alternating or one line edited repeatedly.
- Apply the one-sentence constraint that matches the signature (table below).
- If context is polluted, double-tap
Esc(or run/rewind) to roll back to a clean checkpoint, or/clearand restart with a tight goal. - Prevent the next one with iteration caps in
CLAUDE.mdand aStophook.
Common causes
Ordered by hit rate, highest first.
1. Flaky test — pass / fail / pass / fail
The single most common trigger. A test uses Date.now(), network calls, randomness, or snapshot timestamps. The same code passes one run, fails the next. The agent sees red → edits → green → next run red → reverts → green → edits → red. Forever.
How to spot it: Stop the agent (Esc). Run the same command 3 times yourself without touching code. If you can reproduce green/red/green, the test is the problem, not the model’s edit.
2. Ambiguous prompt — agent oscillates between interpretations
“Fix the login bug” — but you have OAuth and magic-link. Agent fixes OAuth, magic-link test fails. Switches to magic-link, OAuth test fails. Bounces between mutually exclusive interpretations.
How to spot it: Look at the last 5 diffs. If they touch two different files in different features, each reverting the previous, the prompt is under-constrained.
3. Plan is wrong but agent refuses to replan
Agent committed to “change schema → change API → change UI.” Step 1 is impossible (table has data, can’t drop column). Agent stays on step 1 rewriting migrations forever instead of replanning.
How to spot it: Ask the agent “what’s your current plan + which step are you on?” If it says “still on step 1” for 5+ iterations, force a replan.
4. Context saturated with old errors
Long session, most of the context window is stale “previous failure” logs. The agent fits to old errors that no longer reflect the current code state. A telltale sign: you see Autocompact is thrashing: the context refilled to the limit... — Claude Code’s own message when auto-compaction succeeds but a large file or tool output immediately refills the window. Per Anthropic’s troubleshooting docs, Claude Code stops retrying after compaction refills the limit three times in a row, specifically to avoid wasting API calls on a loop with no progress. The usual culprit is the agent re-reading one oversized file or a huge tool output every turn.
How to spot it: Run /context to check the percentage of the window consumed (the breakdown shows tokens spent on system prompt, tools, CLAUDE.md, and message history). If you’re past ~70% and still stuck, the context is likely the cause. Confirm by running /clear (or a fresh session), pasting the current code, and restating the goal — if it succeeds first try, pollution was the problem.
5. Agent ping-pongs between mock and real
Writes a mock to make test pass, next iteration decides mock is unrealistic, edits real implementation, original test breaks, goes back to mock. Loop.
How to spot it: Search the diff trail for jest.mock / vi.mock / MagicMock being added and removed across runs.
6. Tool call failed but agent didn’t notice
Bash returned a non-zero exit code. Agent parsed stdout as success and ran the same command again. Loop because the “failure signal” was invisible.
How to spot it: Check exit codes of last 3 tool calls. Non-zero but Claude didn’t say “command failed” = it missed the failure.
Shortest path to fix
Ordered by ROI. Step 1 + 2 break most loops in under a minute.
Step 1: Stop the agent, read the last 10 actions
Interrupt first. Esc stops the current action and returns you to the prompt; Ctrl+C cancels a hung command. If a single press doesn’t land, press again — the first request is graceful, repeated presses escalate to a harder kill. If the terminal is fully unresponsive, close it and run claude --resume in the same directory to pick the session back up; restarting does not lose the conversation.
Once stopped, ask:
List the last 10 actions (file path + edit summary + command + exit code).
No commentary. Just a table.
Most of the time you’ll see two files alternating, the same line edited repeatedly, or repeated test runs without code progress. The pattern reveals the loop signature.
Step 2: Break with a one-sentence constraint
Match the loop signature to its constraint:
| Loop type | Constraint prompt |
|---|---|
| Edits same test expectation | ”Don’t touch the test. The test is right — change the implementation to match.” |
| Adds/removes mocks | ”Keep the real implementation. Delete all mocks and run the integration test.” |
| Rewrites migrations | ”Schema is frozen. Restart the plan but only touch the UI layer.” |
| Bouncing two files | ”You may only edit src/auth/login.ts. All other files are read-only.” |
| Same flaky test failing | ”Stop running this test. Mock the time-dependent dependency, then move on.” |
| Re-running failed command | ”The last command exited non-zero. Read the error, don’t re-run blindly.” |
Step 3: For flaky tests, stabilize the test first
Don’t let the agent loop on a flaky signal. Stabilize:
# Run 5 times — is it stable?
for i in {1..5}; do pnpm test -- --testNamePattern="login flow"; done
If 2+ fail, mock the source of nondeterminism:
// vitest setup
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-01-01'));
Then resume the agent with the now-stable test.
Step 4: Reset context — rewind, clear, or compact
When the trace is dominated by stale failure logs, you have three escalating levers. Pick by how much state you want to keep:
| Command | What it does | Use when |
|---|---|---|
Double-Esc or /rewind | Opens the rewind menu. Every prompt is a checkpoint; restore conversation, code, or both. | You want to undo the loop’s edits and retry from a known-good point. |
/compact [focus] | Summarizes history and replaces it, keeping the gist. Pass a focus, e.g. /compact keep only the plan and the diff. | Context is large but the goal/plan is still correct. |
/clear | Wipes conversation history entirely. Your code changes stay intact — /clear does not revert files. | The whole session is polluted and you want a blank slate. |
Two things to know about /rewind before you rely on it (confirmed in Anthropic’s checkpointing docs, June 2026). First, double-Esc only opens the rewind menu when the prompt input is empty; if you’ve typed something, the first Esc clears the text and the second opens the menu. Second, checkpoints only track files changed by Claude’s own edit tools — files modified by a Bash command (rm, mv, a script, a migration that wrote to disk) are not captured and cannot be undone by rewind. For those, lean on Git. Checkpoints persist across sessions and are auto-cleaned after 30 days.
After /clear (or in a brand-new session), restart with a tight, self-contained prompt:
Goal: [one sentence]
Current code: [paste the full file, not a diff]
Failing test/error: [paste verbatim]
Constraints: don't touch tests; only edit src/X.ts.
Print the plan first; wait for approval before editing.
A clean context isn’t anchored to old failure logs, so it usually succeeds on the first try. Note that /rewind restoring “code only” reverts file changes while keeping the conversation — handy when the discussion was useful but the edits went sideways.
Step 5: Set iteration caps
Prevent the next loop. The soft version is a rule in CLAUDE.md:
## Agent behavior constraints
- Maximum 5 build/test iterations per task
- If failing after 3 iterations, stop and report
- Do not edit the same block in the same file more than 3 times
- After 3 retries of any tool call, stop and ask
Cursor uses its Project Rules / .cursor/rules, Codex uses AGENTS.md — same idea. A CLAUDE.md rule is advisory, though; the agent can talk itself out of it. For a hard stop, add a Stop hook. The Stop event fires when Claude finishes responding.
Watch the direction here, because it is easy to get backwards (the hooks reference, June 2026): for a Stop hook, exit code 2 and {"decision": "block"} prevent Claude from stopping and force it to continue — the opposite of what you want for a loop guard. To halt the session at your cap, emit {"continue": false, "stopReason": "iteration cap reached"} on stdout (any exit code). A small script that reads an iteration counter and prints {"continue": false, ...} once the count passes your cap gives you a guardrail the model cannot override. Do not use exit 2 to stop a loop — it does the reverse and keeps the agent running.
Step 6: Force replan on stuck plans
If the agent is on step 1 forever:
Stop. The current plan is failing. Don't continue.
Print:
1. Why step 1 isn't working
2. Two alternative approaches
3. Recommend one
Wait for my approval before continuing.
This forces the agent out of the failed plan instead of grinding on it.
How to confirm the loop is actually broken
A loop can look fixed for one turn and resume on the next. Before you walk away, check three things:
- The trace moves forward. Ask for the last-10-actions table again. You should see new files or new lines, not the same edit repeated. Same line edited 3+ times = still looping.
- The signal is deterministic. Run the target check yourself, by hand, 3 times in a row. If it’s not stable green or stable red, you stabilized nothing — the flaky test is still feeding the agent noise.
- The completion criterion is met, literally. Not “looks done” — the exact command you gave as the goal exits 0. Run
pnpm test -- auth(or whatever you specified) and confirm the exit code yourself.
If all three hold, the loop is broken. If any one fails, you’re one prompt away from the next loop.
Prevention
- Set iteration caps in
CLAUDE.md, and back them with aStophook so the loop has a built-in, non-overridable exit. - Isolate flaky tests into a
flaky.test.tsso agents never iterate on an unstable signal. - Force a replan after 3 consecutive failed attempts on the same step.
- Give explicit completion criteria — not “fix login” but “
pnpm test -- authexits 0”. - Inspect the agent’s action trace, not just the final output. Loops show up in the trace long before they show up in results.
- When a loop appears, double-
Escto a clean checkpoint or/clearand restart, rather than fighting a polluted session. - Watch
/context; proactively/compactpast ~70% so you never hit the auto-compact thrashing wall mid-task. - If thrashing is driven by one giant file, ask the agent to read it in a line range instead of whole, or push that work to a subagent that runs in its own context window.
FAQ
How do I stop Claude Code when it’s stuck in a loop?
Press Esc to stop the current action, or Ctrl+C to cancel a hung command. If one press doesn’t work, press again — repeated presses escalate to a harder kill. If the terminal is fully frozen, close it and run claude --resume in the same directory to continue the same session.
What’s the difference between /clear, /compact, and /rewind?
/clear wipes the conversation history but leaves your code changes in place. /compact summarizes the history and replaces it (you can pass a focus, e.g. /compact keep only the plan and the diff). /rewind (or double-Esc) opens a checkpoint menu where you restore the conversation, the code, or both to an earlier prompt. Use /rewind to undo bad edits, /compact to shrink a still-valid session, and /clear to start fresh without losing your files.
Does Claude Code create automatic checkpoints?
Yes. As of June 2026, every prompt you send creates a checkpoint, and double-tapping Esc (or /rewind) lets you roll back to any of them — conversation only, code only, or both.
Why does the same flaky test keep looping the agent?
A flaky test gives the agent a non-deterministic pass/fail signal, so it edits, sees green, then red on the next run, and reverts — endlessly. Stabilize the test first (mock the time/network/randomness, e.g. vi.useFakeTimers()), then resume. Never let the agent iterate against a signal that changes on its own.
Can I hard-cap iterations so loops can’t happen?
Yes. A CLAUDE.md rule is advisory, but a Stop hook is enforced by Claude Code itself: emit {"continue": false, "stopReason": "..."} once an iteration counter passes your cap, and the agent cannot continue past it. Do not reach for exit code 2 here — on a Stop hook, exit 2 (and {"decision": "block"}) forces Claude to keep going, which is exactly the loop you are trying to stop. {"continue": false} is the one that halts the session.
Related
- Claude Code execution prompts
- Plan mode issues
- Claude Code beginner guide
- Claude Code workflow
- Claude Code project setup
External references: