When Cursor can’t fix a bug it introduced, the fastest way out is to stop asking it and look at what it actually changed. Cursor’s agent mode edits several files at once and shows you a diff most people accept without reading. The bug is almost always in an edit you didn’t look at — so the first tool isn’t another prompt, it’s git diff.
This guide is specific to Cursor because its failure modes are specific. Composer and agent mode are extraordinarily productive, and they produce a recognisable set of problems that a general “debug AI code” approach misses.
Why Cursor bugs are different
Three things about how Cursor works shape the bugs it leaves behind:
- It edits across files in one go. A single request can touch six files. You review the one you were thinking about and accept the rest.
- It works from a context window, not from your codebase. If the helper it needs is in a file it didn’t read, it writes a new one. Now you have two, and they’ll diverge.
- It optimises for the request, not the system. Ask it to make a test pass and it may edit the test. Ask it to fix an error and it may catch and swallow it.
None of these are Cursor being bad. They’re what a tool does when it can see part of a system and is told to make a symptom go away.
Step 1: See what it actually did
Before touching anything, get a complete picture of Cursor’s edits since things last worked.
# Every file changed since your last known-good commit
git diff --stat HEAD
# The actual changes — read all of it, not just the file you asked about
git diff HEAD
If you’ve been accepting edits without committing for a while, this diff can be large. That is itself the finding: you have an unknown number of unreviewed changes in your working tree, and that’s where the bug lives.
If there’s no git history at all, initialise one now, before doing anything else. You can’t bisect what you didn’t record.
git init && git add -A && git commit -m "Snapshot before debugging"
From here on, commit after every Cursor session that leaves things working. It’s the single habit that makes Cursor bugs cheap instead of expensive.
Step 2: Find the edit that broke it
If you have a few commits, let git find the culprit instead of reading everything:
git bisect start
git bisect bad # current state is broken
git bisect good <commit-hash> # a commit where it worked
# git checks out a midpoint — run the app, then:
git bisect good # or: git bisect bad
# repeat until git names the commit
git bisect reset
With ten commits this takes about four checks. It answers “which change broke it” without any opinion about why, which is the right order to do things in.
Step 3: Check the four places Cursor code fails
Once you’re looking at the right change, these account for most Cursor-specific bugs.
Hallucinated APIs
Cursor will confidently call methods that don’t exist, or import packages that were never installed, especially for less common libraries or recent versions.
// Looks fine. `parseWithSchema` is not a real export.
import { parseWithSchema } from 'zod'
Check the actual package docs, not Cursor’s memory of them. And check package.json — an import that resolves in your editor because of a stale node_modules can still fail on a clean install.
Duplicated helpers
Search for the function name across the codebase. If it exists twice, Cursor wrote the second one without seeing the first. They’ll agree today and drift after the next edit.
grep -rn "function formatDate\|const formatDate" src/
Keep one, delete the other, and fix the imports. This is dull work and it prevents an entire category of “it works in one place and not the other” bugs.
Tests that were edited to pass
If you asked Cursor to fix a failing test, check whether it fixed the code or the test.
git diff HEAD -- '**/*.test.*' '**/*.spec.*'
A changed assertion, a widened matcher, or a new .skip is not a fix. It’s a bug with the alarm turned off.
Swallowed errors
Cursor’s most common “fix” for a crash is to catch the exception. Search the diff for new try/catch blocks and check what happens in the catch.
// This made the error message go away and made the bug permanent.
} catch (e) {
console.log(e)
}
If the catch doesn’t recover meaningfully, remove it and let the real error surface.
Step 4: Fix it with Cursor — but differently
Once you know what’s wrong, Cursor is good at the fix. The difference is in how you ask:
- Point at the specific file and line, and use
@fileto include the other file that it needs to agree with. The bug is at a seam; give it both sides. - Tell it what not to touch. “Fix the type mismatch in
api/orders.ts. Do not modifyschema.tsor any test.” - Ask for one change, review the diff properly, commit, then ask for the next.
- Add a
.cursorrulesfile with the conventions it keeps breaking — naming, where helpers live, “never modify tests to make them pass.” It reads this on every request.
Mistakes to avoid
- “Fix it” as the whole prompt. Cursor will change something. It might not be the right something.
- Accepting agent-mode diffs unread. This is how the working tree fills with unreviewed edits.
- Letting it run without git. No history means no bisect, no diff, no undo.
- Asking it to fix a test. Ask it to fix the code that the test is testing.
- Trusting an import because the editor didn’t complain. Run
npm installfrom clean and build.
Checklist
- I can see every change Cursor made since it last worked (
git diff) - I know which edit broke it (bisect, or reading the diff)
- I’ve checked for imports and methods that don’t exist
- I’ve searched for duplicated helpers
- No test was modified to make it pass
- No new
catchblock is hiding the real error - The fix is one change, reviewed, and committed on its own
When it’s worth getting help
If the diff is enormous, if you never had git history to bisect against, or if the bug touches auth, payments or data, someone reading the whole thing once is faster than another afternoon of prompts. That’s the specific thing I do — see AI code debugging.