Playbook
The fastest way to make a bug worse is to ask an AI to "fix it" before anyone knows what "it" is. The agent guesses at a cause, changes something plausible, the symptom shifts, and now you have two bugs and a diff you can't trust. Debugging with an agent isn't about a magic prompt — it's about a discipline that stops the guessing. The discipline is old and boring and it works: reproduce, root-cause, verify. Skip any of the three and you're not debugging, you're gambling.
We debug with agents daily, and the teams that struggle almost always skip step one. They describe the bug in prose and let the model imagine the rest. The teams that fly make the bug happen first, on purpose, before touching a single line.
Step 1: Reproduce first — the exact failing command or input
You cannot fix what you cannot trigger on demand. Before anything else, find the precise recipe that makes the bug happen every time: the exact command, the exact input, the exact steps. "It fails sometimes when I save" is not a reproduction; "running npm run import fixtures/bad.csv throws on row 12" is. The reproduction is the whole foundation — it's what you'll use to confirm the fix later, and without it you'll never actually know you're done.
Hand the agent the reproduction, not your theory:
- The exact command or user action that triggers it.
- The full error message and stack trace — all of it, pasted, not summarized.
- What you expected to happen instead.
- What you'd already tried, so it doesn't retread dead ends.
If the bug is intermittent, your first job is to make it reliable — narrow the input, pin the state, find the smallest case that fails consistently. An unreproducible bug isn't ready to fix yet; it's ready to reproduce, and that's the task.
Step 2: Root-cause — read the code paths, don't pattern-match the symptom
This is where agents go wrong on their own, and where you have to steer. The symptom ("undefined is not a function") tells you where the crash surfaced, not why. The root cause is upstream — the place a value got set wrong, a condition that was never handled, an assumption that broke. A fix that silences the symptom without addressing the cause is a landmine: the bug moves, mutates, or returns next week wearing a different hat.
Direct the agent to trace, not to patch. Ask it to read the actual code paths from the error backward: where does this value come from, what set it, what upstream call produced the bad state. Make it cite the specific lines. A good root-cause finding sounds like "the value is null because loadUser() returns null for deleted accounts and the caller never checks" — a specific mechanism at a specific place — not "there might be a null issue somewhere." If the agent proposes a fix before it can explain the mechanism, stop it and ask for the mechanism first. A fix without a named cause is a guess.
Step 3: The smallest correct fix
Once you know the true cause, the fix should be small and targeted — the least change that addresses the root, and nothing else. This is the moment agents love to "improve" things: refactor the neighbors, rename variables, tidy adjacent code. Don't let them. A debugging change should touch as little as possible, because every extra line is a new place for a new bug to hide and a bigger diff to verify. Fence the agent: "fix only the root cause we identified, in these files, change nothing else." Cleanups are a separate task for a separate, calmer moment.
Step 4: Verify — the repro is gone AND nothing else broke
Verification has two halves and both are mandatory. First, re-run the exact reproduction from step 1 — the same command, the same input — and confirm the bug is actually gone. Not "the agent says it's fixed"; you run it. This is why the reproduction had to be precise: it's now your proof. Second, run the broader tests and a quick sanity check of related features to confirm the fix didn't break something else. A fix that resolves the bug and quietly breaks two neighbors is not a fix; it's a trade.
Make the agent show its work: paste the real output of the reproduction now passing, and the real output of the test suite still green. "Should be fixed now" is not evidence. A command that used to fail and now succeeds is.
The copy-paste debug prompt
Here's the prompt we hand an agent to run the whole loop. It bakes the discipline in so the agent can't skip to guessing.
We have a bug. Follow this order strictly and do not skip steps.
REPRODUCTION
- Trigger: <exact command or steps that make it fail>
- Error / wrong behavior: <full error + stack trace, pasted>
- Expected instead: <what should happen>
- Already tried: <dead ends to avoid>
STEP 1 — Confirm the reproduction.
Run the trigger yourself and paste the failing output. Do not
propose a fix yet.
STEP 2 — Find the ROOT cause.
Read the code paths from the error backward. Explain the exact
mechanism and cite the specific file:line where the cause lives.
Do not fix the symptom. Wait for me to confirm the cause.
STEP 3 — Smallest correct fix.
Change only what's needed to fix that root cause, only in the
files involved. No refactors, no cleanups, no extra changes.
STEP 4 — Verify.
Re-run the reproduction and paste the now-passing output.
Then run the full test suite and paste that output too.
If anything else broke, stop and report it.
The template feels heavy for a one-line typo and it's exactly right for anything that isn't. The order is the value: it physically prevents the agent from jumping to a fix before it understands the problem.
Why the order is non-negotiable
Every part of this exists to defeat a specific failure. Reproduce-first defeats fixing a bug you can't actually trigger. Root-cause-before-fix defeats the symptom patch that moves the bug instead of killing it. Smallest-fix defeats the debugging session that becomes an accidental refactor. And two-part verification defeats the "fixed" that broke something else. Follow the four steps and debugging stops being a coin flip.
Once the fix lands, the natural next move is a fresh set of eyes on the diff — ideally an agent that didn't write it. That's the AI code review workflow, and it catches the class of mistakes the debugger, having just convinced itself, can't. And if you'd rather not create the bug in the first place, the AI coding mistakes to avoid covers the habits that keep this loop rare.