Course outline · 0% complete

0/32 lessons0%

Course overview →

Reading a Traceback

lesson 9-1 · ~11 min · 27/32

Recall from lesson 8-2 what happens when a function without a return statement is called and its result printed: None appears.

Falling off the end of a function hands back None, and nothing about that is an error. The program keeps running, which is exactly what makes it a silent surprise rather than a loud one.

This unit deals with the loud kind. When Python genuinely cannot continue it stops and reports why, and the rest of this lesson is about reading that report.

The traceback is a map, not an insult

A working engineer spends a large share of every day reading error output, and the ones who read it calmly fix bugs in minutes instead of hours. Python's crash report is genuinely helpful once you know the reading order, so this lesson is about extracting the answer it is already giving you.

When Python hits something impossible, it stops and prints a traceback. Read it bottom-up:

Traceback (most recent call last):
  File "main.py", line 3, in <module>
    print(age + 3)
TypeError: can only concatenate str (not "int") to str
  • Last line first: the error type (TypeError) and a plain-English message.
  • Line above it: the file, line number, and the exact code that failed.

The usual suspects:

ErrorTypical cause
NameErrortypo, or using a variable before assigning it
TypeErrormixing types, like "17" + 3
ValueErrorright type, bad content: int("hello")
IndexErrorlst[10] on a short list
KeyErrordict lookup for a missing key
ZeroDivisionErrordividing by 0

The first line, about the most recent call, becomes useful once functions are involved. With nested calls Python lists every frame from the outermost inward, so the bottom-most file line is where the error actually happened and the ones above it show the path that led there.

Traceback (most recent call last): File "main.py", line 3 print(age + 3)TypeError: can only concatenate…1. read this first:what went wrong2. then this:where it happened
Read a traceback bottom-up: the last line names the error, the lines above point to the exact file and line.

A crash reporting ValueError: invalid literal for int() with base 10: 'twelve' was caused by a line like n = int(input()), where the user typed twelve.

The error type is the clue. ValueError means the argument was the right type, since int() does accept a string, but its content could not be converted. The message even quotes the offending text, 'twelve', which is not a sequence of digits.

Contrast that with the alternatives, because each error type points somewhere different. Dividing by zero raises ZeroDivisionError, indexing past the end of a short list raises IndexError, and using a name that was never assigned raises NameError. Matching the type to the situation usually narrows a crash to one or two candidate lines before you even look at the line number.

Fixing a TypeError

This program stops on line 2 with TypeError: can only concatenate str (not "int") to str, and the message names the problem precisely.

age = "17"
print(age + 3)

The value in age is a string, so + tries to join text and is handed a number instead. Python refuses to guess whether the intent was arithmetic or concatenation. Converting first, with the tool from lesson 3-1, resolves the ambiguity.

age = "17"
print(int(age) + 3)

Output

20

Now int(age) produces the number 17 and + performs addition. The alternative reading, joining "17" and "3" into "173", would have required age + str(3) instead, which is why the language leaves the choice to you.

Fixing a NameError

This program stops with NameError: name 'message' is not defined. Did you mean: 'mesage'?, and the suggestion at the end has effectively solved it already.

mesage = "all tests passed"
print(message)

A NameError means the name being used was never assigned. Line 1 creates mesage and line 2 asks for message, so the two never meet. Python noticed the near match and offered it, which is a feature worth reading rather than skimming past.

message = "all tests passed"
print(message)

Output

all tests passed

The fix corrects the definition rather than the usage, since the correctly spelled name is the one worth keeping. Renaming the other way would work identically as far as Python is concerned, but it would leave a misspelling in the code for the next reader to trip over.