How to Write a Bug Report for an AI Coding Agent
The difference between a two-line fix and a sprawling rewrite is usually four sentences you did not bother to write.
Write five things and an agent will usually fix the right thing: what you observed, what you expected, the exact trigger, the boundary of what it may change, and the list of files it must not touch. Leave any of them out and the model fills the gap with a guess, which is how a bug report about a date format turns into a refactor of your entire formatting layer.
Why the format matters more than it did with humans
A colleague who receives a vague bug report asks you a question. An agent does not, or not often enough. It reads the ambiguity, picks the interpretation that makes the most textual sense, and starts editing with complete confidence. Its willingness to act is the feature you are paying for and the reason a loose report is expensive.
The second difference is scope. A human fixing a date bug fixes the date. An agent sees three other places with a similar pattern and helpfully corrects all of them, which is either excellent or a review nightmare depending on whether you asked. This is the same behaviour behind an agent changing code you did not ask it to touch, and the report is where you head it off.
The five parts of a bug report an agent can act on
Observed. What happened, quoted exactly. Error text, stack trace, the wrong value as printed. Not your paraphrase of it.
Expected. What should have happened, stated as a concrete value rather than an adjective. Not correct formatting, but the string 2026-08-10.
Trigger. The precise input, route, click or command that produces it, and whether it fails every time or intermittently.
Scope. Which file or module the fix belongs in, or if you genuinely do not know, the smallest area you are confident it lives inside.
Do not touch. The files, patterns or behaviours that must stay exactly as they are, including tests you do not want rewritten to pass.
The same bug, twice
The vague version
The dates on the invoice page are wrong. Please fix.An agent handed that will find every date in the project. It will discover three formatting helpers, decide they should be one, write a new utility, migrate the call sites, and update the snapshot tests that now fail. The diff touches a dozen files. Somewhere inside it, possibly, is the actual fix. Reviewing it costs more than writing the fix by hand would have.
The precise version
Observed: /invoices/1842 renders "Due 08/10/2026" in the Due column.
Expected: "Due 10 Aug 2026", matching every other date in the app.
Trigger: any invoice whose due date is in the current month. Reproduces every time.
Scope: the fix belongs in src/components/InvoiceTable.tsx, which formats this column inline instead of calling formatDate() from src/lib/dates.ts.
Do not touch: src/lib/dates.ts, any other component, or the existing snapshot tests.That report produces a two-line change: an import and a swapped call. It took ninety seconds longer to write and saved an hour of review. The scope line is doing most of the work, because it converts an open question into a closed one.
Give evidence, not your theory
The most common way a good report goes wrong is leading with a diagnosis. If you write that the caching layer is stale, the agent will investigate the caching layer whether or not that is where the problem lives, and it will find something to change there because there is always something to change.
Keep your hypothesis, but demote it:
Put the observed behaviour first and the theory last, explicitly labelled as a hypothesis.
Include the evidence that produced the theory, so the agent can reach a different conclusion from the same facts.
Say what you already ruled out and how, otherwise it will check those things again.
Paste real output rather than describing it. A stack trace carries file paths and line numbers that your summary throws away.
Scope boundaries are the part people skip
Boundaries feel bureaucratic until the first time an agent rewrites a test to make it pass. Phrase them as positive constraints where you can, since models follow instructions about what to do far more reliably than instructions about what to avoid.
Boundary | Weak phrasing | Phrasing that works |
|---|---|---|
Files | Do not touch other files | Confine every change to src/components/InvoiceTable.tsx |
Tests | Do not break the tests | Leave all existing tests unmodified and make them pass as written |
Dependencies | Do not add packages | Use only the dependencies already in package.json |
Size | Keep it small | The fix should be under ten changed lines. If it cannot be, stop and explain why |
Refactoring | No refactoring | Fix only this bug. List other improvements you noticed as a comment rather than making them |
That last row is the most useful line in the table. It gives the agent somewhere to put the urge to tidy, which stops the urge leaking into the diff.
A template worth keeping
Paste this above any bug you hand to an agent. It takes under two minutes to fill in and it removes the three questions the agent would otherwise answer by guessing.
Observed: [exact output, error text or wrong value, quoted]
Expected: [the concrete correct value, not an adjective]
Trigger: [the input, route or command; every time or intermittent]
Scope: [the file or module the fix belongs in]
Unchanged: [files, tests and behaviours that must stay as they are]
Ruled out: [what you already checked, and how]
Hypothesis:[optional, clearly labelled, placed last]The ruled-out line is the one people add last and end up valuing most. Without it, the agent repeats your investigation from the beginning, which costs tokens, costs time, and occasionally produces a change to something you had already checked and cleared. Two words per item is enough: what you tried, what you saw.
When it still gets it wrong
Revert cleanly rather than iterating on top of a bad change. A second attempt on a polluted working tree compounds the problem.
Ask what it believed the cause was before you correct it. The answer usually shows which part of your report was ambiguous.
Add the missing fact to the report and run it again from a clean state.
If two attempts fail, the bug is probably not where you think it is. Ask for a diagnosis with no edits, then write a new report from what comes back.
On a long session, quality also degrades for a reason that has nothing to do with your writing: the agent loses track of earlier context. If reports that worked this morning stop working this afternoon, context loss in a large codebase is the more likely culprit, and a fresh session beats a longer prompt. The behaviour differs by product too, which is worth knowing when comparing the current crop of AI coding tools.
Worth stealing
Simon Tatham's essay How to Report Bugs Effectively predates all of this by decades and remains the best thing written on the subject. Its central instruction, report symptoms rather than deductions, applies unchanged. If your team keeps a shared instructions file, the AGENTS.md convention is a reasonable place to put the house format so nobody retypes it.
Questions that come up
Is this not just a good bug report?
Largely, yes. The difference is that a colleague will ask before rewriting your test suite and an agent will not, so the scope and do-not-touch sections carry weight they never needed to carry before.
What if I do not know which file the bug is in?
Say so, and give the smallest area you are confident about. Then ask for a diagnosis with no code changes first. Splitting investigation from repair keeps the eventual fix small, and it is faster than reviewing a speculative patch.
Should I include the failing test?
If you have one, it is the single most valuable thing in the report, because it converts a description into something checkable. If you do not, asking for a failing test before the fix is a reasonable first instruction. Just be explicit that the test must fail for the right reason.
Does this apply to feature requests too?
The scope and do-not-touch sections do, and they matter more, since a feature has no error message anchoring it. Observed and expected become current behaviour and desired behaviour, and the trigger becomes the user action that should reach the new path. General debugging technique for AI-written code covers the rest.
How did this land?
About the author

Developer Advocate
Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.


