# When Claude Code goes sideways

Web page: https://academy.memberpress.ai/courses/code-intro/l8/

Lesson 8 of 8 in [Claude Code — Getting Started](https://academy.memberpress.ai/courses/code-intro/), 10 min.

Video: https://www.youtube.com/watch?v=tzq6BX2VRb0

Three failure modes appear often enough that naming them before you encounter them is worth the time. They have different symptoms, different causes, and different fixes. Recognizing them early saves the session.

**Over-eager refactoring**

Symptom: the diff is much larger than the task. You asked Claude to extract a service layer and the diff touches the router, the models, three test files, and a README. Claude interpreted the task as an invitation to improve the architecture rather than execute the brief.

Cause: the brief did not contain a boundary clause, or the boundary was too vague ("don't change other things" rather than "don't touch files outside src/api/user.ts and src/services/").

Fix: interrupt with `Escape`, run `git checkout .` to reset, and rewrite the brief with an explicit boundary. Do not attempt to "undo" specific changes from a sprawling diff — the reset is faster and cleaner.

What the revised brief looks like:

```
Refactor: Move DB calls from UserController into UserService.
Boundary: Only src/api/UserController.ts and src/services/UserService.ts (create it).
Do not touch src/routes/, src/models/, or any test file.
Success: npm test passes, UserController has no import from src/db/.
```

**Hallucinated APIs**

Symptom: Claude's code looks syntactically correct and logically plausible, but it fails at runtime with `TypeError: X is not a function` or a 404 from an internal endpoint. The method or route does not exist.

Cause: Claude's training data contains many examples of similar libraries and APIs. When an API is unfamiliar or has changed since training, Claude will sometimes generate a call to a method that it expects to exist by analogy.

Fix: ask Claude to show you where in the source or documentation the method is defined.

```
> You called `db.user.findByEmail()`. Show me where that method is defined 
> in this codebase or in the Prisma docs.
```

If Claude cannot show you the source, the method is hallucinated. Delete the generated code and re-brief with the real API reference included in your message:

```
> The correct Prisma method is `db.user.findFirst({ where: { email } })`.
> Rewrite the service using that call.
```

Including a concrete example of the real API in the brief prevents the hallucination from recurring.

**The "almost done" loop**

Symptom: Claude says "almost done" or "one more fix" multiple times. The same test keeps failing. Each attempt tries a different approach. None of them read the actual error message carefully.

Cause: Claude is iterating on the code based on its prediction of what might fix the problem, rather than reading the specific failure the test is reporting. This is a reasoning failure, not a capability failure — it has the information it needs, but it is not using it.

Fix: interrupt. Read the test output yourself.

```bash
npm test 2>&1 | grep -A 10 "FAIL\|●"
```

Then give Claude the specific failure, not a description of it:

```
> Stop iterating. The test failure is:
> Expected: { status: 404, body: { error: "User not found" } }
> Received: { status: 200, body: { id: null } }
> The findFirst call is returning null instead of throwing. 
> Fix the null check in UserService.findByEmail only.
```

Naming the specific location and the specific discrepancy ends the loop. Claude is not stuck because it cannot reason — it is stuck because it is not reading the signal you are giving it.

**Recognizing which failure you are in**

| Symptom | Failure mode |
|---|---|
| Diff is larger than the task | Over-eager refactoring |
| Code looks right but runtime crashes | Hallucinated API |
| Same test fails repeatedly, different attempts | "Almost done" loop |

**If you get stuck beyond these three**

Close the session. Run `git checkout .` to reset. Start a new session with a smaller, more bounded brief. Every Claude Code problem that resists these fixes is a brief problem, not a Claude problem.

**Cross-reference**

l5 ("When to interrupt, when to let it run") covers the interrupt mechanics. l4 ("Asking for a refactor") covers brief construction. The failure modes here are what those two lessons are trying to prevent.

**Deliverable**

Write down which of these three failure modes you have encountered — in Claude Code or in any other tool that generates code. For each one: what were the symptoms, what did you do, and what would you do differently now.

**Further reading**

- ["Plausible Code That Doesn't Compile"](https://sethshoultes.com/blog/agents-hallucinate-apis.html) — the deeper case for why hallucinated APIs happen and what changes in the brief stop them recurring.
- ["Switch Channels, Don't Dig"](https://sethshoultes.com/blog/switch-channels-dont-dig.html) — the three-strike rule for the "almost done" loop: when the model keeps missing, change channel instead of adding constraints.

## Try these prompts

- Describe the three Claude Code failure modes — over-eager refactoring, hallucinated APIs, and the "almost done" loop — in one sentence each, covering symptom and fix.
- Write the Claude Code message to send when it calls db.user.findByEmail() and that method does not exist. Ask it to show you the source before proceeding.
- Write the git commands to interrupt a sprawling session and reset to a clean state before trying a tighter brief.
