Programming with AI
ENRU
2.12

Reading a traceback

finding the guilty line in thirty seconds

What it is

A traceback is what Python prints when a program crashes. It looks intimidating, reads in half a minute, and almost always names the guilty line outright.

The skill pays twice: you understand immediately what happened, and you can hand the AI not "it doesn't work" but the exact cause. The second matters more — it decides whether it gets fixed on the first try or the fifth.

What it looks like in code

Here is a program that requests the weather and shows the temperature:

import json


def fetch_weather(city: str) -> dict:
    # The server's reply when the city is not found
    raw = '{"cod": "404", "message": "city not found"}'
    return json.loads(raw)


def format_report(data: dict) -> str:
    temp = data["main"]["temp"]
    return f"It is {temp} degrees right now"


def handle_command(city: str) -> str:
    data = fetch_weather(city)
    return format_report(data)


if __name__ == "__main__":
    print(handle_command("Tonsberg"))

Run it, and you get:

measured
Traceback (most recent call last):
  File "/tmp/tbdemo2/weather2.py", line 21, in <module>
    print(handle_command("Tonsberg"))
          ~~~~~~~~~~~~~~^^^^^^^^^^^^
  File "/tmp/tbdemo2/weather2.py", line 17, in handle_command
    return format_report(data)
  File "/tmp/tbdemo2/weather2.py", line 11, in format_report
    temp = data["main"]["temp"]
           ~~~~^^^^^^^^
KeyError: 'main'

How to read it

Rule one: read bottom to top. The last line is the point; everything else is how we got here.

measured
KeyError: 'main'

KeyError means: something reached for a dictionary key that isn't there. The key is named outright — 'main'.

Rule two: the second-to-last block is the crash site.

measured
  File "/tmp/tbdemo2/weather2.py", line 11, in format_report
    temp = data["main"]["temp"]
           ~~~~^^^^^^^^

File, line 11, function format_report. The marks under the line are Python's own hint (since 3.11): the ^^^^ points at exactly the part of the expression that failed. Here you can see the problem is ["main"], not ["temp"]. That matters: without the marks you would be guessing which of the two lookups was to blame.

Rule three: the blocks top to bottom are the call path. The program started at line 21, went into handle_command (line 17), from there into format_report (line 11), and crashed. That path answers the question "where did the data come from" — and that is almost always the real cause.

Rule four: look for your own files. In a real traceback half the lines come from libraries: site-packages, aiogram, asyncio. You can scroll past them. Look at the last line that mentions your file: the error is almost always there, even when the crash happened inside a library.

What we learned

Line 11 is not at fault — it honestly does what it was asked. Line 6 is at fault: the server replied "city not found" and the code decided it had received the weather. The crash site and the cause are different places. The traceback shows the first; the call path helps you find the second.

What to tell the AI

The most common mistake is retelling the error in your own words.

Don't
doesn't work, throws some key error

The AI has to guess: which key, in which file, on which line. It will start rewriting everything in sight.

The right way is to hand over the whole traceback, adding what it does not contain: what you did and what you expected.

Better
Crashes when requesting the weather for a city that doesn't
exist. Expected the reply "City not found", got an exception.

Traceback (most recent call last):
  File "weather.py", line 21, in <module>
    print(handle_command("Tonsberg"))
  File "weather.py", line 17, in handle_command
    return format_report(data)
  File "weather.py", line 11, in format_report
    temp = data["main"]["temp"]
           ~~~~^^^^^^^^
KeyError: 'main'

Fix it so that the server error is handled in fetch_weather,
rather than surfacing in format_report.

That last sentence matters. Without it the AI will most likely wrap line 11 in a try/except or substitute data.get("main", {}). It stops crashing, the user gets "It is None degrees right now", and the real error goes into hiding.

What to look for

The AI muffles the error instead of handling it. The signs in generated code: except Exception: pass, except: return None, .get(key, {}) instead of a check. The code stops crashing and starts lying.

The AI fixes the crash site, not the cause. If it only changed the line the traceback pointed at, ask: "where did the wrong data reach this function from?"

A truncated traceback. Copy only the last line and the AI never sees the call path, so it fixes blind. Copy the whole thing, starting at the word Traceback.

An asynchronous traceback is longer and messier: it holds asyncio frames and library internals. Same rule — find the last line with your own file in it.

Cheat sheet

  • Read bottom to top: the last line says what happened.

  • The second-to-last block is the file and line of the crash; ^^^^ marks the guilty part of the expression.

  • Scroll past other people's files, find the last one of yours.

  • Crash site ≠ cause. Look for the cause further up the call path.

  • Give the AI the whole traceback plus what you did and what you expected.