Debugging with AI works best when you give the model what you would give a colleague at your desk: the whole error, the code that raised it, what you expected, what happened instead, and what you have already ruled out. The model cannot run your program or see your data, so everything it knows about the bug comes from what you paste. The most useful single habit is to ask for the cause before the fix.
Paste everything, not only the error
The two tabs below describe the same bug. A script reads a CSV file exported from Excel and crashes with KeyError: 'email', even though the file clearly has an email column.
A KeyError means the dictionary has no key called 'email'. You can avoid the error by using .get(), which returns None instead of raising:
email = row.get("email")
Or check first:
if "email" in row:
email = row["email"]
The first reply is correct about what a KeyError is, and its advice is harmful here. With row.get("email") the crash disappears, and every email becomes None. The model answered the most common version of the error because that is all the prompt described.
The full report worked because of two lines. "Only the first column fails" and "saved from Excel as CSV UTF-8" are the clues that point to the byte order mark, and neither is in the error message. You rarely know which of your details is the clue, which is why you paste all of them.
The five things to include
- The complete error. The whole traceback or console output, not a paraphrase. The line numbers and the call chain show where the failure started, which is often a few frames away from where it surfaced.
- The code that raised it. The failing function and the code that calls it. If the error mentions a line, make sure that line is in what you paste.
- Expected versus actual. One sentence each. For bugs with no error at all (wrong output, a blank page, a slow query), this is the whole bug report.
- Your environment. Language version, operating system, framework and library versions when they could matter. Encoding, path and date bugs often depend on the platform.
- What you already tried. This stops the model from suggesting it again, and the result of each attempt is evidence. "Removing the email part makes it work" narrowed the CSV bug to one column.
Ask for the cause before the fix
A fix request invites the model to change code until the error goes away. A cause request asks it to reason about why the error happens, and the fix follows from that. Ask for a short ranked list of causes with a way to check each one, then run the checks yourself. This template does that:
TypeError: Cannot read properties of undefined (reading 'map')function UserList() {
const [users, setUsers] = useState();
useEffect(() => {
fetch("/api/users")
.then((res) => res.json())
.then((data) => setUsers(data));
}, []);
return (
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}- The component renders before any data exists.
useState()starts asundefined, and React renders the component once before the effect runs, sousers.mapis called onundefined. That alone produces exactly your error, on every load. Check: addconsole.log(users)just beforereturn. If it logsundefinedand the error follows straight away, this is it. - The API returns something other than an array. If it returned an object such as
{ users: [...] }, the error would be different (users.map is not a function), so this is not the error you pasted. It can still be the next problem once cause 1 is fixed. Check:console.log(data)inside the second.then. - The request fails. Not involved yet: because the first render throws, React never runs the effect, so the request is not even sent. Check: the Network tab shows no
/api/usersrequest, which also confirms cause 1.
The reply does something a fix-first answer skips: it rules causes out because the error they would produce differs from the one you pasted, or because the code never gets far enough for them to happen. That is also why the exact text of the error matters. Asking the model to list causes before it answers is a light form of chain-of-thought prompting: the reasoning comes first and the conclusion rests on it.
Make a minimal reproduction
A minimal reproduction is the smallest program that still shows the bug: hardcoded data instead of a database call, one function instead of the whole module. Building one often finds the bug before you ask anyone, because each piece you remove either keeps the bug (it was not involved) or makes it vanish (it was). When it does not, the reproduction is the ideal prompt: short enough for the model to read every line, and free of unrelated code that could send it chasing the wrong problem.
If the bug depends on data, include a few rows that trigger it. A model can reason about [{"id": 1, "name": null}]; it cannot reason about "some rows in production".
When the fixes stop working
If the model's third fix fails in the same way, a fourth request for a fix is unlikely to do better. Two things help more:
- Give it new evidence. Add a print or log line that shows the actual values at the point of failure, run it, and paste the output. Evidence that contradicts the model's theory is the fastest way to a better one.
- Start a fresh conversation. Long debugging threads fill up with abandoned theories and old versions of the code, and the model may keep building on them. A new chat with the current code, the error, the evidence and a line saying "already ruled out: X and Y" often gets further in one reply.
Be careful with confident answers about library behavior. A model can describe an option or a function that does not exist; see AI hallucination for how to check. When the code is not broken but you do not understand why it does what it does, a prompt for explaining code is the better tool.
Frequently Asked Questions
How do I ask ChatGPT or Claude to fix my code?
Paste the complete error message and the code that raised it, then add three short lines: what you expected, what happened instead, and what you already tried. Ask for the most likely cause and how to confirm it before asking for a fix. A bare "fix this" gets a fix for the most common version of the error, which may not be yours.
Should I paste my whole project into the AI?
No. Paste the function where the error happens, the code that calls it, and a sample of the data it receives. Better still, cut the problem down to the smallest program that still shows it. Unrelated files make the answer slower and give the model more places to look for a problem that is not there.
Why does the AI's fix make the error go away but the program still does not work?
The fix treated the symptom. For example, replacing row["email"] with row.get("email") stops a KeyError, but if the key is missing because of a bug upstream, every email is now silently None. Asking for the cause first, and for a way to confirm it, avoids fixes that only hide the problem.
What if the AI keeps suggesting fixes that do not work?
Stop asking for fixes and give it evidence instead. Report what each attempt changed, add a print or log line that shows the actual values, and paste that output. If the conversation is long, start a new one with a clean summary: the code, the error, the evidence and the fixes already ruled out.