Skip to content

Error Recovery: Handling Failures and Getting Unstuck

Claude just refactored your authentication module. It touched six files, updated the tests, and then — the type checker shows 23 errors. The tests that were passing five minutes ago now fail across three test suites. Claude tries to fix the errors but introduces new ones. You are two iterations deep into a fix-the-fix spiral and the codebase is worse than when you started. You need to get back to a known good state, understand what went wrong, and try again with a better approach.

This guide covers every recovery mechanism Claude Code gives you: checkpoints for undoing edits, session management for restarting cleanly, strategies for breaking out of error loops, and workflows for preventing cascading failures in the first place.

  • The Esc-Esc checkpoint rewind that undoes Claude’s changes instantly
  • A workflow for recovering from cascading test failures without losing progress
  • Strategies for breaking Claude out of fix-the-fix loops
  • Context management techniques when sessions get too long
  • Preventive patterns that reduce the need for recovery in the first place

Every time Claude edits a file, it snapshots the previous contents before making changes. These checkpoints are your safety net.

The fastest recovery: press Esc twice. This opens the checkpoint rewind interface, letting you scroll back through Claude’s edits and restore any previous state. Each checkpoint shows what changed, so you can pick exactly which point to revert to.

This is separate from git. You do not need to have committed anything. Checkpoints cover all file edits Claude made during the current session.

# After pressing Esc twice, you see something like:
# [3] Edited src/lib/auth.ts - refactored session handler
# [2] Edited src/lib/auth.ts - added rate limiting
# [1] Edited src/api/routes/login.ts - updated login handler
#
# Select a checkpoint to revert to that state

If you prefer to stay in the conversation:

Undo the last change you made to src/lib/auth.ts.

Or to undo everything from a specific action:

Undo all the changes from the authentication refactor.
Revert every file you touched back to how it was before.

The most common failure mode: Claude introduces an error, tries to fix it, introduces a new error, fixes that, and the code gets progressively worse with each iteration. Here is how to break out.

The moment you notice Claude is going in circles, interrupt:

Stop. Do not make any more changes. The last three attempts have
made things worse. Let me assess where we are.

Then ask Claude to evaluate the current state without making edits:

This forces Claude to reason about the problem instead of reactively patching symptoms.

Sometimes the cleanest path forward is to undo everything and start over with a different approach:

Revert all changes from this session. Then let us try a different
approach to the authentication refactor. Instead of restructuring
the entire module, change only the session handling logic and
leave the rest untouched.

A fresh attempt with a narrower scope almost always produces better results than trying to salvage a broken refactor.

If Claude keeps failing with the same strategy, redirect explicitly:

Stop trying to fix the type error by changing the interface.
Instead, update the implementation in auth-service.ts to match
the existing interface. The interface is correct; the
implementation is wrong.

Claude often needs you to tell it which side of a conflict is authoritative. Without direction, it may keep oscillating between changing the interface and changing the implementation.

When multiple tests fail after a change, the failures are usually connected. Ask Claude to find the root cause:

This prevents Claude from trying to fix each test individually, which often leads to inconsistent patches across test files.

When you have many failures, recover incrementally:

  1. Run the type checker first — Type errors are the most common root cause of test failures after a refactor. Fix these before even looking at test results.

  2. Run one test file at a time — Start with the test file closest to the code you changed. Fix that before moving to dependent test files.

  3. Verify after each fix — Run the full suite after fixing each test file to see if the fix resolved failures elsewhere too.

  4. Stop when green — Do not keep “fixing” tests that are already passing.

Run npx tsc --noEmit first. Fix all type errors. Then run
the tests for @src/lib/auth.test.ts only. Fix any failures.
Then run the full suite to see what else cleared up.

Long sessions accumulate context: file reads, command outputs, failed attempts, backtracked approaches. Eventually Claude’s responses degrade because the important information is buried under noise.

When Claude starts losing track of your project’s patterns or repeating earlier mistakes:

/compact focus on the authentication refactor and current test failures

The /compact command summarizes the conversation and frees up context space. The focus argument tells Claude what to prioritize in the summary, so the important context survives compaction.

If compaction is not enough, clear the entire conversation:

/clear

Then immediately re-establish context:

I was working on refactoring the authentication module. The
current state is:
- I changed src/lib/auth.ts to use async session handling
- The type checker passes
- Two tests in auth.test.ts are still failing
- The failures are related to mock setup for the new async functions
Read the failing tests and fix the mock setup.

If a task takes more than 30-40 minutes, break it into separate sessions rather than fighting context degradation:

  • Session 1: Schema and database migration
  • Session 2: API route implementation
  • Session 3: Tests and integration
  • Session 4: Review and PR

Start each session fresh with claude, not claude -c. Give Claude a brief summary of what was done in previous sessions and what the current task is.

If Claude Code exits unexpectedly, your conversation history is preserved. Resume with:

Terminal window
claude -c

This continues the most recent conversation. All previous messages and tool results are restored.

If you want to try a different approach without losing your current session:

Terminal window
claude --continue --fork-session

This creates a new session with the same conversation history up to this point. The original session remains unchanged. You can try the alternative approach in the fork and go back to the original if it does not work.

If you have multiple sessions from different attempts:

Terminal window
claude --resume

This opens a session picker. Use B to filter by current branch, making it easy to find the session you want.

The best recovery is not needing recovery. These patterns reduce failures before they happen.

The single most effective habit:

After making each change, run the type checker and relevant tests.
Do not move to the next step until the current step passes. If
something fails, fix it before continuing.

This catches errors when they are small and isolated, not after they have cascaded across the codebase.

Before risky operations, create a git commit:

Commit the current working state with a message like
"checkpoint: before auth refactor". Do not push this commit.

If everything goes wrong, you can git reset --soft HEAD~1 to return to this point while keeping all the changes staged.

Instead of “refactor the authentication module,” try “change only the session expiry logic in auth.ts, keeping the rest of the module unchanged.” Narrower scope means fewer files touched, fewer potential errors, and easier recovery.

Before any complex change:

Before making changes, explain exactly what you plan to modify
and why. List every file you will touch and what the change
will be in each file.

Catching a bad plan is much cheaper than reverting a bad implementation.

Esc-Esc does not show checkpoints — Checkpoints only exist for the current session. If you started a new session, the checkpoints from the previous session are gone. Use git history to recover instead: git diff to see what changed, git checkout -- <file> to revert specific files.

Claude keeps repeating the same failed approach — Use /clear and start fresh. Describe the problem from scratch and explicitly rule out the approach that was not working: “Do not restructure the module. Instead, make the minimal change needed to fix the timeout issue.”

/compact loses important context — Add a focus argument: /compact focus on the database migration and the three remaining test failures. Without a focus, compaction may drop details you need. For critical context, add it to your CLAUDE.md so it persists across sessions.

Tests pass locally but Claude introduced subtle bugs — After any significant change, ask Claude to review its own work: “Review all changes you made in this session. Check for edge cases, null handling, and error paths that might be missing.” This self-review catches issues that tests miss.

Session becomes unusable due to context bloat — Start a new session with claude (not claude -c). Give Claude a brief summary and the specific files to read. Five minutes of re-establishing context is faster than fighting a degraded session for thirty minutes.

You have now completed the Claude Code quick-start guide. You can install Claude Code, authenticate, configure permissions, set up your IDE, initialize your project with CLAUDE.md, plan features from a PRD, use extended thinking for hard problems, connect MCP servers, build features incrementally, manage version control, and recover from failures.