Aa Syntax Errors and Runtime Errors

This lesson distinguishes the three kinds of error, covers the specific causes of syntax errors and why their locations mislead, and works through runtime errors by symptom — nothing happening, hanging, and exceptions.

Subject: Python · 65 slides · code lesson

Open the interactive version of this deck

What this lesson covers

The lesson, slide by slide

1. Lesson Aa Syntax Errors and Runtime Errors

Title

Python · Appendix A — Debugging

§A.1-A.2, pp. 193-197

2. By the end of this lesson you can

Objectives

Five things, each one you can check yourself at an interpreter prompt.

Think Python, 2nd edition — Allen B. Downey §A.1-A.2, pp. 193-197 — the pages these objectives are drawn from

3. Before we start: three ways a program can be wrong

Warm-up

You have met all three, without them being named together.

Discussion prompt

A program can fail to run at all, stop partway with a message, or run to completion and produce the wrong answer. What is different about diagnosing each?

Hint: Which one gives you the least to work with?

Answer:

The first two hand you a message and a location — imperfect information, and information. The third gives you nothing but a wrong result.

And they are found at different moments: one before the program runs at all, one during, and one only if somebody checks the output.

Which is why the appendix opens by insisting on the distinction: the first step in debugging is to figure out which kind of error you are dealing with, because the techniques differ.

4. The one idea behind this appendix: identify the kind first

Concept

When you are debugging, you should distinguish among different kinds of errors in order to track them down more quickly.

The first step in debugging is to figure out which kind of error you are dealing with. Although the appendix is organised by error type, some techniques are applicable in more than one situation.

Figure (svg): The three kinds of error with when each is discovered

Each is discovered at a different moment, which is what makes the diagnosis differ.

Think Python, 2nd edition — Allen B. Downey §A.1-A.2, pp. 193-193

5. The three kinds of error

Section

Section 1

6. Discovered at three different moments

Concept

The three kinds differ in when they are found, and each of the book's examples is one you have already met.

# syntax:  omitting the colon at the end of a def
#          -> SyntaxError: invalid syntax

# runtime: an infinite recursion
#          -> maximum recursion depth exceeded

# semantic: an expression evaluated in an unexpected order
#          -> an incorrect result, and no message
KindWhat is wrongWhen it is found
syntaxthe structure is wrongfound before running
runtimesomething goes wrong while runningfound during
semanticit runs and does the wrong thingfound by checking

The book's syntax example is worth noting for its verdict as much as its content: omitting the colon at the end of a def statement generates the somewhat redundant message SyntaxError: invalid syntax.

Think Python, 2nd edition — Allen B. Downey §A.1-A.2, pp. 193-193

7. Picture it: what each kind gives you

Picture it

Information decreases from left to right.

Figure (svg): Two columns contrasting errors that report themselves with the one that does not

The first two announce themselves. The third is the reason for testing.

Which is why the appendix gives the third its own long section, and why chapters 11 and 16 kept recommending self-checks.

8. Worked example: classifying an error from its symptom

Worked example

The symptom names the kind before anything else is known.

# 'it won't even start'          -> syntax
# 'it stops with a traceback'    -> runtime
# 'it runs and the answer is wrong' -> semantic
# 'it runs forever'              -> runtime (a hang)
SymptomThe kindNote
no execution at allsyntaxfound in translation
execution then a messageruntimefound during
execution then a wrong answersemanticfound by checking

Ask whether it ran.

Why: If not, the interpreter rejected it while translating the source code into byte code — a syntax error.

Ask whether it stopped with a message.

Why: That is a runtime error, and the message includes where it occurred and what functions were executing.

Ask whether the answer is wrong.

Why: A program that runs without producing error messages but doesn't do the right thing has a semantic error.

Figure (svg): The state of the program after each line of Worked example classifying an error from its symptom, drawn as a ladder with one rung per traced line

The whole run at once: each drop is one line of the program.

Three questions, answerable before looking at any code. That classification is the first step, and it decides which techniques apply.

Verify: Check the hang against the classification.

Why: A program that runs forever is a runtime error even though it never stops — the appendix treats it under runtime errors, because execution began. An infinite recursion eventually produces a message and an infinite loop may not, which is why the two get separate diagnoses.

9. Sort: which kind of error?

Sorting

Ask when it would be discovered.

Sort into buckets

For each situation, which kind of error is it?

syntax
a missing colon after a def; an unclosed bracket
runtime
an infinite recursion; indexing a list past its end
semantic
an expression evaluated in an unexpected order; a program that prints the wrong total
syn
Both are structural, so the interpreter finds them while translating the source code into byte code — before the program runs at all.
run
Both go wrong while the program is running, so execution starts and then something fails or never finishes.
sem
Both run without producing error messages and do the wrong thing, which is the definition — and nothing detects them automatically.

10. Worked example: the same mistake, three kinds

Worked example

One idea about what a program should do, wrong three ways.

if x > 0
    print('positive')
# syntax: no colon

if x > 0:
    print(y)
# runtime: NameError, if y does not exist

if x > 0:
    print('negative')
# semantic: runs, and says the wrong thing
VersionWhat is wrongThe kind
the missing colonthe structure is wrongsyntax
the undefined namewrong while runningruntime
the wrong wordruns correctly, means wronglysemantic

Break the structure.

Why: The interpreter cannot translate it, so nothing runs at all.

Break a reference.

Why: The structure is fine, and the program fails when it reaches the line.

Break the meaning.

Why: Everything is legal and the program does exactly what it says, which is not what was meant.

Figure (svg): A flowchart classifying an error by whether the program ran and what it produced

Two questions, and the kind is settled before any code is read.

Three errors from one intention. The third is the only one nothing can detect, which is why it needs a different approach entirely.

Verify: Ask which one a computer could find.

Why: The first two, both automatically — the interpreter finds one before running and reports the other during. The third requires knowing what the program was supposed to do, which is information the program does not contain.

11. Trap: applying the wrong technique to the kind

Trap

The trap

A program produces a wrong answer, and the developer reads the error message carefully.

Start with the message

Why: Which is the right first move for the other two kinds.

There is no message — a semantic error is a program that runs without producing error messages. Waiting for one, or re-reading output for a diagnosis it does not contain, is time spent on the wrong technique.

The fix

Identify the kind, then choose the technique.

A message and a location: read them, carefully

Why: For syntax and runtime errors, they are the best evidence available.

No message: compare what it does with what it should

Why: Which is the whole of semantic debugging, and the next lesson's subject.

The appendix's first instruction is exactly this: the first step in debugging is to figure out which kind of error you are dealing with. The techniques are organised by kind because they do not transfer.

12. Predict: which kind is this?

Prediction

The program runs and produces an answer.

def average(t):
    return sum(t) / len(t) - 1
# the -1 was not meant to be there
AspectWhat is trueNote
the syntaxcorrectit runs
no exceptionnothing raisesthe arithmetic is legal
the answerwrong by one

Predict first

Which kind of error is this?

  • Semantic — it runs without producing error messages and does the wrong thing
  • Syntax — something is wrong with the structure
  • Runtime — something goes wrong while it is running
  • None; the program is correct

Correct: Semantic — it runs without producing error messages and doesn't do the right thing.

Why: Every step is legal, so nothing is reported: the interpreter translated it and the arithmetic succeeded. That silence is the defining feature — a semantic error can only be found by comparing what the program does with what it was supposed to do.

13. Complete it: the first debugging step

Faded example

Before any technique.

Fill in the blanks

# the first step in debugging is to figure out
# which kind of error you are dealing with

Why: The appendix opens with this instruction because the techniques are organised by kind and do not transfer: reading an error message is the right first move for two of the three, and useless for the one that produces none.

14. Explain it yourself: why does the kind change the technique?

Explain it to yourself

The appendix is organised by it.

Discussion prompt

Explain why knowing which kind of error you have changes what you should do next.

Hint: What evidence does each one give you?

Answer:

Because each kind supplies different evidence. A syntax error gives a location and an unhelpful message; a runtime error gives a location, a kind of exception and a traceback; a semantic error gives nothing but output.

So the first two start from reading, and the third starts from comparing — deciding what the program should have produced and finding where it diverged.

Which is why the appendix says to identify the kind first. Starting with the wrong technique is not merely slower; for a semantic error it means waiting for a message that is never coming.

15. Reading a syntax error

Section

Section 2

16. Where Python noticed, not where the error is

Concept

Syntax errors are usually easy to fix once you figure out what they are. Unfortunately, the error messages are often not helpful.

SyntaxError: invalid syntax
SyntaxError: invalid token

# neither of which is very informative
PartHow usefulNote
the messagerarely says what is wronginvalid syntax
the locationdoes say somethinga line number
what the location meanswhere Python NOTICEDnot necessarily where the error is

On the other hand, the message does tell you where in the program the problem occurred. Actually, it tells you where Python noticed a problem, which is not necessarily where the error is — sometimes the error is prior to the location of the error message, often on the preceding line.

Think Python, 2nd edition — Allen B. Downey §A.1-A.2, pp. 193-194

17. Picture it: the gap between noticing and causing

Picture it

The reported line may be innocent.

Figure (svg): A panel showing a syntax error reported on the line after the actual mistake

Which is the single most useful thing to know about syntax errors: the line number is a starting point for a search backwards, not an answer.

18. Worked example: why the location drifts

Worked example

An unclosed bracket is the clearest case.

x = (1 + 2
y = 3

# Python continues with the next line as part
# of the current statement, so the error appears
# almost immediately in the next line
LineWhat Python doesNote
the open bracketthe statement is unfinishedline 1
the next lineread as a continuationstill line 1's statement
the reportline 2where it stopped making sense

Note what an open bracket does.

Why: An unclosed opening operator makes Python continue with the next line as part of the current statement.

Note where it breaks.

Why: Generally, an error occurs almost immediately in the next line — because that line makes no sense as a continuation.

Note the consequence.

Why: The reported line is the innocent one, and the mistake is above it.

Figure (svg): The state of the program after each line of Worked example why the location drifts, drawn as a ladder with one rung per traced line

The whole run at once: each drop is one line of the program.

A report one line below the cause. Knowing the mechanism means the search direction is obvious rather than guessed.

Verify: Check what an unterminated string does instead.

Why: It may cause an invalid token error at the end of your program, or it may treat the following part of the program as a string until it comes to the next string — in which case it might not produce an error message at all. So the drift can be much larger than one line, and sometimes there is no message.

19. Predict: which line is at fault?

Prediction

The error is reported on line 2.

x = (1 + 2
y = 3
# SyntaxError on line 2
LineWhat happensNote
line 1an unclosed bracketthe statement continues
line 2read as a continuationand makes no sense
the reportline 2where Python noticed

Predict first

Where is the actual error?

  • Line 1 — the unclosed bracket makes Python read line 2 as a continuation
  • Line 2, exactly where it is reported
  • Both lines are wrong
  • Neither; the message is spurious

Correct: Line 1 — an unclosed opening operator makes Python continue with the next line as part of the current statement.

Why: The message tells you where Python noticed a problem, which is not necessarily where the error is. Here the drift is one line, which the book says is common — and an unterminated string can push it much further, or produce no message at all.

20. Worked example: the checklist

Worked example

Eight specific causes, worth running through.

# 1. a Python keyword used as a variable name
# 2. a missing colon at the end of a compound header
# 3. unmatched or curly quotation marks
# 4. an unterminated triple-quoted string
# 5. an unclosed (, { or [
# 6. the classic = instead of == in a conditional
# 7. mixed spaces and tabs in the indentation
# 8. non-ASCII characters pasted from elsewhere
GroupWhat they have in commonNote
the first sixstructuraleach with a characteristic symptom
the seventhinvisiblewhich is why repr and a good editor help
the eighthpasted textusually from a web page

Run through the list rather than staring.

Why: Syntax errors are usually easy to fix once you figure out what they are, and the list is short enough to check.

Note the two invisible ones.

Why: Mixed spaces and tabs, and non-ASCII characters pasted from a web page — neither is visible in most editors.

Note the recommendation.

Why: The best way to avoid the indentation problem is to use a text editor that knows about Python and generates consistent indentation.

Figure (svg): The eight common causes of syntax errors grouped by how visible each is

Two of the eight cannot be seen, which is why the checklist beats re-reading.

Eight causes, of which two produce no visible difference at all. Checking the list is faster than reading the line repeatedly.

Verify: Check the curly quotes warning against where code comes from.

Why: Pasting from a web page, a document or a chat message is exactly where curly quotes and non-ASCII characters come from — two of the eight causes share one origin. Retyping a pasted line is often faster than inspecting it, since the difference is invisible.

21. Trap: staring at the reported line

Trap

The trap

A syntax error names line 40, and the developer reads line 40 repeatedly finding nothing wrong with it.

Look where the error says

Why: Which is the only location available.

The message tells you where Python noticed a problem, which is not necessarily where the error is. Line 40 may be perfectly correct, and re-reading it can go on for a long time.

The fix

Search backwards from the reported line.

Check the preceding line first

Why: Sometimes the error is prior to the location, often on the preceding line.

And look for an unclosed bracket or quote above

Why: Which is the usual mechanism for the drift.

There is also a stronger clue: if you are building the program incrementally, you should have a good idea about where the error is — it will be in the last line you added.

22. Error analysis: four lines with syntax problems

Error analysis

Mark each and name the cause.

Annotate

  • Line 1 uses a single equals sign in a conditional — the classic = instead of ==, which is item six on the checklist.
  • Line 2 omits the colon at the end of a compound statement header, which is item two and generates the somewhat redundant message SyntaxError: invalid syntax.
  • Line 3 opens with a single quote and closes with a double, so the string is unterminated — item three, and the error may appear some distance later.
  • Line 4 is correct. A for header with a colon is exactly right, and it is included because a checklist is only useful if you can also recognise a line that is fine.
  • So three of the four are on the eight-item list, and each has a characteristic symptom that identifies it faster than re-reading.
  • The two items not represented here are the invisible ones: mixed tabs and spaces, and non-ASCII characters pasted from elsewhere.

Running the checklist is faster than staring, because two of the eight causes cannot be seen at all.

23. Discriminate: visible or invisible?

Discrimination

Some causes cannot be seen.

Sort into buckets

For each cause of a syntax error, is it visible in the source?

visible
a missing colon; an unclosed bracket; = instead of ==
hard or impossible to see
mixed spaces and tabs; a non-ASCII character pasted from a web page; curly quotes instead of straight ones
vis
Each is a character that is present or absent and can be found by reading carefully — which is why re-reading sometimes works.
inv
None shows a visible difference in most editors: whitespace looks the same either way, a pasted character may render identically, and curly quotes look almost like straight ones.

24. Explain it: the line it names looks fine

Explain it

A common and frustrating situation.

Discussion prompt

A classmate has read the reported line twenty times and can see nothing wrong with it. Tell them what to do.

Hint: What does the location actually mean?

Answer:

The line is probably fine. The message tells you where Python noticed a problem, which is not necessarily where the error is — often the error is on the preceding line.

The usual mechanism is an unclosed bracket or quote above: Python continues with the next line as part of the current statement, and the error appears almost immediately in the next line.

So look upward rather than harder, and check the eight-item list — especially the invisible causes, since staring cannot find mixed tabs and spaces or a pasted non-ASCII character however long you look.

25. Are you running the code you are editing?

Section

Section 3

26. The deliberate error test

Concept

If the interpreter says there is an error and you don't see it, that might be because you and the interpreter are not looking at the same code.

# put an obvious and deliberate syntax error
# at the beginning of the program

!!!!!!

# now run it again.
# If the interpreter doesn't find the new error,
# you are not running the new code.
StepWhat it tells youNote
the deliberate errorunmissableat the top
if it is reportedyou are running this filelook elsewhere
if it is notyou are running something elsethe real problem

Check your programming environment to make sure that the program you are editing is the one Python is trying to run. This is the best trick in the appendix, because it distinguishes two situations with identical symptoms.

Think Python, 2nd edition — Allen B. Downey §A.1-A.2, pp. 194-195

27. Picture it: one symptom, two causes

Picture it

Changes making no difference has two explanations.

Figure (svg): Two columns contrasting a real bug you cannot see with code that is not being run

The deliberate error test separates them in one run.

Which matters because the second is invisible from inside the code: every technique for finding a bug is useless if the bug is not in the file you are reading.

28. Worked example: the five likely culprits

Worked example

When the test says you are not running the new code.

# 1. you edited the file and forgot to save
# 2. you changed the filename and are running the old one
# 3. the development environment is misconfigured
# 4. your module shares a name with a standard module
# 5. you imported it earlier and it was not re-read
CauseWhat happensNote
savingthe commonestsome environments do it for you
the name clashyours is shadowedor shadows one
the stale importchapter 14's warningrestart the interpreter

Check the simplest first.

Why: You edited the file and forgot to save the changes before running it again — some programming environments do this for you, but some don't.

Check the names.

Why: If you are writing a module and using import, make sure you don't give your module the same name as one of the standard Python modules.

Check the import.

Why: If you are using import to read a module, remember that you have to restart the interpreter or use reload to read a modified file — if you import the module again, it doesn't do anything.

Figure (svg): A flowchart using a deliberate error to decide whether the file is being run

One run, and two situations with identical symptoms are separated.

Five specific causes, each with a specific fix. The last is chapter 14's warning, and it is the one that catches people in an interactive session.

Verify: Match the symptom to the import case.

Why: A fix that has no effect at all — not a different wrong result, but byte-for-byte the same behaviour — is the signature of an unread file, which lesson 14c named. The deliberate error test confirms it in one run rather than by reasoning.

29. Predict: what does the test tell you?

Prediction

A deliberate error, and no new message.

# added at the top of the file:
!!!!!!

# run it: the original error is reported,
# and nothing about line 1
ObservationWhat it meansNote
the deliberate errorunmissableif this file were read
not reportedthe file is not being read
the conclusionyou are running something else

Predict first

What does this tell you?

  • You are not running the code you are editing
  • The deliberate error is not a syntax error
  • The original bug is in a different function
  • Python ignores errors at the top of a file

Correct: You are not running the code you are editing — if the interpreter doesn't find the new error, the file being run is not this one.

Why: That is the whole point of the test: it distinguishes a bug you cannot see from a file that is not being read, which have identical symptoms. The five likely culprits are an unsaved edit, a renamed file, a misconfigured environment, a module name clash, and a stale import.

30. Worked example: the last resort

Worked example

When nothing else works.

# start again with a new program like 'Hello, World!'
# and make sure you can get a known program to run.
# Then gradually add the pieces of the original
# program to the new one.
StepWhat it establishesNote
a known-good programestablishes a baselinedoes anything run?
adding piecesone at a timeuntil it breaks
the breaking piecethe problemisolated

Establish that anything runs.

Why: Get a known program to run, which confirms the environment works at all.

Add the original's pieces gradually.

Why: Each addition either keeps working or breaks, and the one that breaks is the problem.

Note what this is.

Why: Bisection by construction — the same idea as lesson 11c's scale down the input, applied to the program rather than to the data.

Figure (svg): The state of the program after each line of Worked example the last resort, drawn as a ladder with one rung per traced line

The whole run at once: each drop is one line of the program.

A method that works when understanding has failed, because it needs no hypothesis — only the ability to tell whether the program runs.

Verify: Ask what makes this a last resort.

Why: It discards the work of getting the program written and rebuilds it, which is expensive — and it is guaranteed to locate the problem, which nothing else on the list is. That trade is why it comes last rather than first.

31. Trap: assuming a saved-looking file is saved

Trap

The trap

A developer edits a file, runs it, sees no change, and concludes the fix was wrong.

Trust that the edit took effect

Why: The change is visible on the screen.

Some programming environments save for you and some don't — so what is on screen and what is on disk can differ. The fix may have been perfectly correct and never executed.

The fix

Test it rather than assuming it.

Put an obvious and deliberate syntax error at the beginning

Why: And run it again.

If the interpreter doesn't find it, you are not running the new code

Why: Which redirects the whole investigation.

This takes seconds and rules out a category of imaginary bug. It is worth doing early rather than after an hour, because the two situations are indistinguishable from the code itself.

32. Sort: which culprit fits the situation?

Sorting

Five reasons the code you run may not be the code you edit.

Sort into buckets

For each situation, which is the likely cause?

a stale import
you are working in an interactive session and imported the module earlier; importing it again produces no change at all
a name problem
you renamed the file this morning; your module is called random.py; your module is called string.py
unsaved changes
the editor shows an unsaved-changes marker
imp
Both are the chapter 14 warning: if you import a module that has already been imported, Python does nothing and does not re-read the file. Restarting the interpreter is the safe fix.
name
Each is about which file is found — a renamed file leaving the old one in place, or a module sharing a name with one of the standard Python modules.
save
The commonest of all: you edited the file and forgot to save the changes before running it again.

33. Complete it: the deliberate error test

Faded example

Put something unmissable where it will be read first.

Fill in the blanks

# add an obvious and deliberate syntax error
# at the beginning of the program, then run it again

Why: It goes at the beginning so that it is reached before anything else — if the interpreter doesn't find the new error, you are not running the new code. Putting it further down would risk the original error being reported first, which would leave the question unanswered.

34. Where checking the obvious first pays

Real world

A category of problem that looks like a different problem.

Discussion prompt

Think of a time something appeared broken and the real issue was that you were looking at the wrong thing entirely. How long did it take to notice?

Hint: The wrong file, the wrong copy, the wrong window.

Answer:

Editing one copy of a document while reading another, changing a setting in the wrong profile, testing against a stale version — and the delay is usually long, because every observation is consistent with the change simply not working.

What makes it slow is that the evidence never contradicts the wrong hypothesis. Nothing about the code says you are not reading me.

Which is why a cheap test that distinguishes them is worth so much. The deliberate syntax error takes seconds and settles a question that could otherwise absorb an hour of looking at correct code.

35. Runtime symptoms: nothing, or forever

Section

Section 4

36. Two symptoms with specific diagnoses

Concept

Once your program is syntactically correct, Python can read it and at least start running it. What could possibly go wrong?

The first may be intentional if you only plan to import this module to supply classes and functions — which is exactly what the __name__ guard from chapter 14 arranges.

Think Python, 2nd edition — Allen B. Downey §A.1-A.2, pp. 195-195

37. Picture it: diagnosing a hang

Picture it

Two possibilities, and a test for each.

Figure (svg): A flowchart distinguishing an infinite loop from an infinite recursion

An infinite recursion announces itself eventually; an infinite loop does not.

Which is why waiting is a diagnostic step: the message that does or does not arrive distinguishes the two.

38. Worked example: is it even reaching the loop?

Worked example

Two prints, and the question is answered.

print('entering the loop')
while ...:
    ...
print('exiting the loop')

# first message and not the second:
#   you've got an infinite loop
What you seeWhat it meansNote
neither messagethe loop is never reacheda flow problem
the first onlyan infinite loopconfirmed
boththe loop is not the problemlook elsewhere

Bracket the suspect loop.

Why: Add a print immediately before that says entering the loop and another immediately after that says exiting the loop.

Run it and read which appeared.

Why: If you get the first message and not the second, you've got an infinite loop.

Note the third outcome.

Why: Neither message means the loop is never reached at all, which is a flow-of-execution problem rather than a loop problem.

Figure (svg): A panel showing the three possible outcomes of bracketing a loop with prints

Three possible outcomes from two prints, and each points somewhere different. That is what makes it a diagnosis rather than a guess.

Verify: Ask what to do once the loop is confirmed.

Why: Print the values of the variables in the condition and the value of the condition itself at the end of each pass — so you can see the last time through, where the condition should have been False, and why the variables were not being updated correctly.

39. Predict: which message tells you it is an infinite loop?

Prediction

Two prints bracket the loop.

print('entering the loop')
while x > 0:
    ...
print('exiting the loop')
PrintWhen it runsNote
the first printbefore the loopalways runs if reached
the secondafter the looponly if it ends
the diagnosiswhich appeared

Predict first

What pattern of output indicates an infinite loop?

  • The first message and not the second
  • Both messages
  • Neither message
  • The second message and not the first

Correct: The first message and not the second — the loop was entered and never exited.

Why: Neither message would mean the loop is never reached, which is a flow-of-execution problem rather than a loop problem; both would mean the loop finished and is not the cause. Two prints distinguish three situations, which is what makes the technique worth the two lines.

40. Worked example: an infinite recursion

Worked example

Look for the base case, then check it is reached.

def f(n):
    print(n)              # print the parameters
    ...
    return f(n - 1)

# if the parameters are not moving toward
# the base case, you will get some ideas about why not
QuestionHow to answer itNote
is there a base case?a condition that returns without recursingfirst question
is it reached?print the parameterssecond question
the valuesshould approach the base case

Check that a base case exists.

Why: There should be some condition that causes the function to return without making a recursive invocation — if not, you need to rethink the algorithm and identify one.

Check that it is reached.

Why: If there is a base case but the program doesn't seem to be reaching it, add a print at the beginning of the function that prints the parameters.

Read the values.

Why: If the parameters are not moving toward the base case, you will get some ideas about why not.

Figure (svg): The state of the program after each line of Worked example an infinite recursion, drawn as a ladder with one rung per traced line

The whole run at once: each drop is one line of the program.

Two questions in order, and the print answers the second. This is chapter 5's base-case requirement turned into a diagnostic.

Verify: Ask what the printed values look like in each failure.

Why: A missing base case shows parameters marching steadily past where they should have stopped; a base case that is never reached shows parameters that stop changing, or change in the wrong direction. The two look quite different, which is why printing them distinguishes the causes.

41. Trap: assuming a program that does nothing is hanging

Trap

The trap

A file of functions and classes is run, produces no output, and is assumed to be stuck.

Read no output as no progress

Why: Which is what a hang looks like.

The program may have finished immediately: this is most common when your file consists of functions and classes but does not actually invoke a function to start execution. Nothing is stuck, because nothing was asked to happen.

The fix

Check whether it finished or is still running.

A program that returns to the prompt has finished

Why: However little it printed.

Then make sure there is a function call

Why: And that the flow of execution reaches it.

The appendix notes this may be intentional if you only plan to import the module to supply classes and functions — which is exactly what the __name__ guard arranges, and it means the same file behaves differently depending on how it is used.

42. Discriminate: which diagnosis applies?

Discrimination

Match the symptom to the section.

Sort into buckets

For each symptom, which is the likely cause?

nothing was invoked
no output, and the prompt returns immediately; a file of only functions and classes produces nothing
an infinite recursion
it runs for a while then reports maximum recursion depth exceeded; a recursive function never returns
an infinite loop
it runs forever with no message; a while loop never ends
none
Both finished immediately because no function was invoked to start execution — which may be intentional for a module meant only to be imported.
rec
Both are recursion without a reachable base case, and one shows the characteristic message that most of the time an infinite recursion eventually produces.
loop
Both run indefinitely with no message, which is what distinguishes a loop from a recursion — nothing accumulates, so nothing is exceeded.

43. Complete it: diagnose a suspected loop

Faded example

Bracket it and see which message appears.

Fill in the blanks

print('entering the loop')
while x > 0:
...
print('exiting the loop')

Why: If you get the first message and not the second, you've got an infinite loop. The pair distinguishes three cases: neither means the loop is never reached, the first only means it never ends, and both mean it is not the problem.

44. Think it through: why does a recursion report and a loop not?

Socratic

Both run forever.

Discussion prompt

An infinite recursion eventually produces a message and an infinite loop usually does not. Why the difference?

Hint: What accumulates in each?

Answer:

A recursion adds a frame to the stack on every call, so something is accumulating — and eventually there is no room, which produces the maximum recursion depth exceeded error.

A loop reuses the same frame. Nothing accumulates, so nothing runs out, and the program can continue indefinitely without anything to report.

Which makes waiting a genuine diagnostic step: the message that arrives or does not tells you which you have, before any code is read. That is why the appendix's first instruction for a hang is to see whether the error appears.

45. The five common exceptions

Section

Section 5

46. What each one usually means

Concept

If something goes wrong during runtime, Python prints a message that includes the name of the exception, the line where the problem occurred, and a traceback — which traces the sequence of function calls that got you to where you are.

Each name narrows the search before you read a line of code, which is why the first step is always to read which exception it is rather than only where it happened.

Think Python, 2nd edition — Allen B. Downey §A.1-A.2, pp. 196-197

47. Picture it: the exception names the category

Picture it

Five names, five different investigations.

Figure (svg): Five common exceptions with the question each one raises

Reading the name first narrows the search before any code is opened.

And one of them has a special case worth memorising: an AttributeError mentioning NoneType is about the object rather than the attribute.

48. Worked example: the NoneType special case

Worked example

The most useful entry in the whole catalogue.

AttributeError: 'NoneType' object has no attribute 'sort'

# 'If an AttributeError indicates that an object has
#  NoneType, that means that it is None. So the problem
#  is not the attribute name, but the object.'
PartWhat it meansNote
the attribute namenot the problemit may be spelled perfectly
the objectis Nonewhich is the problem
the causesomewhere earlierwhere the None came from

Read the type in the message.

Why: NoneType means the object is None, so no attribute of any name would work.

Redirect the investigation.

Why: The problem is not the attribute name, but the object — so checking the spelling is wasted effort.

Find where the None came from.

Why: You forgot to return a value from a function — if you get to the end of a function without hitting a return statement, it returns None. Or you used the result from a list method, like sort, that returns None.

Figure (svg): A panel decoding an AttributeError that names NoneType

An error about an attribute that is really about an object, with two named causes. Recognising it redirects the whole search.

Verify: Match the two causes to earlier lessons.

Why: Both have appeared: lesson 6a's void function returning None when you assign its result, and lesson 10b's t = t.sort() disaster. So this entry is a catalogue of two traps the course already demonstrated, which is why the message is recognisable rather than cryptic.

49. Predict: what does this error mean?

Prediction

An AttributeError naming NoneType.

AttributeError: 'NoneType' object has no attribute 'sort'
PartWhat it saysNote
NoneTypethe object is Nonethe real problem
'sort'the attribute nameirrelevant here
where to lookwhere the None came fromearlier

Predict first

What is the problem?

  • The object is None — so no attribute of any name would work
  • The attribute name is misspelled
  • Lists have no sort method
  • sort takes arguments that were not supplied

Correct: The object is None — so the problem is not the attribute name, but the object.

Why: The two usual causes are forgetting to return a value from a function, which returns None by default, and using the result of a list method like sort that returns None. Both have appeared earlier in the course, which is why the message is recognisable — and checking the spelling of sort would waste the whole investigation.

50. Worked example: the TypeError catalogue

Worked example

Three quite different causes under one name.

# 1. a value used improperly
#    e.g. indexing with something other than an integer

# 2. a format string mismatch
#    the count or the conversion is wrong

# 3. the wrong number of arguments
#    for methods: check the first parameter is self
CauseWhat it looks likeNote
improper usea value where its type does not fitindexing with a string
format mismatchlesson 14a's three messagescount or type
argument countlesson 17a's subjectself counts

Read the rest of the message.

Why: TypeError covers several situations, and the text after the name distinguishes them.

Check the method case specifically.

Why: For methods, look at the method definition and check that the first parameter is self — then look at the invocation and make sure you are invoking it on an object with the right type.

Note that all three are familiar.

Why: Each has appeared: improper indexing in chapter 8, format mismatches in lesson 14a, and the argument count in lesson 17a.

Figure (svg): The state of the program after each line of Worked example the TypeError catalogue, drawn as a ladder with one rung per traced line

The whole run at once: each drop is one line of the program.

One exception name covering three causes, distinguished by the message text. Reading past the name is what identifies which.

Verify: Check the method advice against lesson 17a.

Why: The reminder to check that the first parameter is self is exactly the takes 2 positional arguments but 3 were given trap — the subject counts as an argument. So the appendix's advice is a compressed version of a lesson already learned, which makes it recognisable rather than new.

51. Trap: reading the location and not the name

Trap

The trap

A traceback appears and the developer goes straight to the line number.

Find where it happened

Why: Which is the concrete information.

The exception name narrows the search far more than the location does — a KeyError and a TypeError on the same line call for completely different investigations, and the name is free to read.

The fix

Read the name, then the location, then the traceback.

The name gives the category

Why: NameError, TypeError, KeyError, AttributeError, IndexError.

The traceback gives the route

Why: It traces the sequence of function calls that got you to where you are.

And for AttributeError, read the type as well: NoneType means the object is the problem rather than the attribute, which sends the search somewhere else entirely.

52. Sort: which exception would this raise?

Sorting

Five names, five categories.

Sort into buckets

For each mistake, which exception does it produce?

NameError
using a variable that was never assigned
TypeError
indexing a list with a string; passing three arguments to a two-parameter method
KeyError, AttributeError or IndexError
looking up a dictionary key that is not there; reading an attribute that was never assigned; an index one past the end of a list
n
The name does not exist in the current environment — check the spelling, and remember that local variables cannot be referred to from outside their function.
t
Both are TypeError's territory: a value used improperly, and the wrong number of arguments to a function.
o
Each names its own category — a missing dictionary key, a missing attribute, and an index greater than the length minus one.

53. Complete it: list an object's attributes

Faded example

When an AttributeError names something you expected to exist.

Fill in the blanks

# AttributeError: check the spelling, or use
print(vars(obj))

Why: You can use the built-in function vars to list the attributes that do exist, which usually makes a misspelling obvious immediately — lesson 15b's diagnostic. It answers a different question from the error: the message says what is missing, and vars says what is there instead.

54. Explain it: my attribute name is definitely right

Explain it

An AttributeError on a correctly spelled name.

Discussion prompt

A classmate has checked the spelling three times and is still getting an AttributeError. What should they read in the message?

Hint: What type does it name?

Answer:

The type before the words object has no attribute. If it says NoneType, the object is None — and the problem is not the attribute name but the object, so the spelling was never relevant.

The two usual causes are a function that reached its end without hitting a return statement, which returns None, and using the result of a list method like sort, which also returns None.

And if the type is something else unexpected — a str where a list was intended, say — that is equally informative: the attribute is missing because the object is not the kind of thing they thought it was.

55. Compare: the three kinds of error

Comparison

Fill the blanks. They differ in when and in what they give you.

Comparison matrix

QuestionSyntaxRuntimeSemantic
When is it found?while translating to byte codewhile the program is runningonly if someone checks the output
Is there a message?yes, often unhelpfulyes, with a tracebackno
Is there a location?where Python noticedwhere it occurred, plus the calls that led therenone
What is the first move?look at and above the reported lineread the exception name, then the tracebackcompare what it does with what it should

The bottom row is why the appendix insists on identifying the kind first: the three first moves have nothing in common.

56. The procedure: diagnosing an error

Pattern

Six steps, and the first two cost almost nothing.

  1. Identify the kind: did it run, did it stop with a message, or is the answer wrong?
  2. If changes seem to make no difference, put a deliberate syntax error at the top and confirm you are running this file.
  3. For a syntax error, check the line and the one above it, then run the eight-item checklist.
  4. For a hang, bracket the suspect loop with prints, or check that a recursion has a reachable base case.
  5. For an exception, read the name before the location — and for an AttributeError, read the type as well.
  6. For no message at all, move to the semantic techniques, which are the next lesson's subject.

Step 2 belongs early rather than late. It takes seconds and rules out a whole category of imaginary bug, and the two situations it distinguishes are indistinguishable from the code itself.

Python documentation — Errors and Exceptions Errors and Exceptions

57. Check yourself 1 of 3: the syntax error location

Check

The reported line looks correct.

Check your understanding

What does a syntax error's line number actually tell you?

  • A. Where Python noticed a problem, which is not necessarily where the error is (correct)
  • B. Exactly which line contains the mistake
  • C. The first line of the file
  • D. Nothing useful at all

Answer: A

Why: Sometimes the error is prior to the location of the error message, often on the preceding line — an unclosed bracket makes Python continue with the next line as part of the current statement, so the error appears almost immediately in the line after the real mistake.

Why B tempts people
This is the natural assumption and it is what makes people re-read a correct line repeatedly.
Why C tempts people
The location is genuinely computed; it is just where the problem became apparent.
Why D tempts people
It is useful as a starting point for searching backwards, which is more than nothing.

58. Check yourself 2 of 3: the deliberate error test

Check

The new error is not reported.

Check your understanding

You add an obvious syntax error at the top of the file, run it, and Python does not mention it. What does that mean?

  • A. You are not running the code you are editing (correct)
  • B. The error is not severe enough to report
  • C. Python checks syntax lazily, line by line
  • D. The original bug is masking it

Answer: A

Why: If the interpreter doesn't find the new error, you are not running the new code — which redirects the whole investigation. The five likely culprits are an unsaved edit, a renamed file, a misconfigured environment, a module name clash, and a stale import.

Why B tempts people
Syntax errors are found while translating the whole file, and severity does not enter into it.
Why C tempts people
The file is translated before it runs, so a syntax error anywhere prevents execution entirely.
Why D tempts people
A syntax error at the top would be found before any other error could occur.

59. Check yourself 3 of 3: NoneType

Check

An AttributeError with a particular type.

Check your understanding

What does AttributeError: 'NoneType' object has no attribute 'x' tell you?

  • A. The object is None, so the problem is the object rather than the attribute name (correct)
  • B. The attribute x is misspelled
  • C. The class does not define x
  • D. x is a method rather than an attribute

Answer: A

Why: If an AttributeError indicates that an object has NoneType, that means it is None — so no attribute of any name would work, and checking the spelling is wasted effort. The two usual causes are a function that returned nothing and the result of a method like sort.

Why B tempts people
The spelling is irrelevant when the object is None; every attribute would fail.
Why C tempts people
NoneType is not your class. The object never became one of your instances.
Why D tempts people
Methods are attributes too, and the same message would appear for either.

60. Where this shows up outside this course

Real world

A report that names where a problem became apparent.

Discussion prompt

Think of a fault reported somewhere other than where it started — a leak, a warning light, a complaint. What made the location misleading?

Hint: Where does the symptom appear?

Answer:

The symptom appears where the effect is visible rather than where the cause is: water surfaces below the pipe, a warning fires when the consequence is detected, a complaint arrives from whoever noticed.

Which is exactly a syntax error's line number: it tells you where Python noticed a problem, which is not necessarily where the error is.

So the useful habit is the same in both settings — treat the reported location as the end of a trail rather than the start, and know which direction the trail runs. For an unclosed bracket, it runs upward.

61. Confidence wager: commit before you check

Commit first

Answer, then rate your confidence. This one saves the most time.

Predict first

Your fix seems to have no effect at all. Before investigating further, what should you do?

  • Put an obvious deliberate syntax error at the top of the file and run it, to check you are running the code you are editing
  • Add more print statements to narrow down the failure
  • Rewrite the function from scratch
  • Restart the computer

Correct: Put an obvious deliberate syntax error at the top of the file and run it, to check you are running the code you are editing.

Why: This is the best trick in the appendix because it separates two situations with identical symptoms: a real bug you cannot see, and a file that is not being executed. If the interpreter doesn't find the new error, you are not running the new code — and no amount of reading, printing or rewriting will help, because the bug is not in the file you are looking at. The five likely culprits are then specific: you edited the file and forgot to save; you changed the filename and are still running the old one; something in the development environment is misconfigured; your module shares a name with one of the standard Python modules; or you imported the module earlier and Python did not re-read it, which chapter 14 warned about. The test takes seconds and belongs early rather than after an hour, precisely because every observation from inside the code is consistent with the wrong hypothesis.

62. Explain it to someone else

Explain it

Three kinds, and three different first moves.

Discussion prompt

A classmate says their program is broken. Before looking at it, what three questions would tell you how to help?

Hint: The appendix's first instruction.

Answer:

Did it run at all? If not, it is a syntax error — the interpreter rejected it while translating, and the reported line may be below the real mistake.

Did it stop with a message? Then it is a runtime error, and the exception name narrows the search before any code is opened — NameError, TypeError, KeyError, AttributeError or IndexError, each with its own question.

Did it finish and give the wrong answer? That is a semantic error, which produces no message at all — so the techniques are completely different, and that is the next lesson.

63. Exit ticket

Exit ticket

One honest answer. It decides what the next lesson opens with.

Predict first

Which of these is still least solid for you?

  • The three kinds of error, and why the distinction matters
  • Syntax errors, and why the reported location misleads
  • The deliberate error test, and the five culprits behind it
  • Diagnosing hangs, and the five common exceptions

Correct: Whichever you picked is the right answer — this one is for you, not for a mark.

Why: The three kinds are worth naming because the appendix's whole structure follows them, and the first move differs completely between them. The syntax error location is the single most useful fact here: it says where Python noticed, not where the error is. The deliberate error test is the one that saves the most time, and it belongs early rather than late. And the exception catalogue is worth reading once so the names become informative — particularly the NoneType case, which sends the search somewhere else entirely.

64. Synthesis: draw the map of this lesson

Connect it up

One page, from memory.

Draw it

Draw a decision tree starting from did it run? and did it stop with a message?, ending at the three kinds of error. Beside the syntax branch, write what the reported line number actually means and two of the eight common causes. Beside the runtime branch, write the five exception names with one question each. Finally write out the deliberate error test in two sentences and list what a negative result would mean.

65. What you can do now

Recap

Five pages, and a diagnostic procedure rather than a set of facts.

If you remember one thingIt is this
From the three kindsIdentify which before choosing a technique; they do not transfer.
From syntax errorsThe line number says where Python noticed, not where the error is.
From the deliberate errorIt separates I cannot see it from I am not running it.
From hangsA recursion accumulates frames and eventually reports; a loop does not.
From exceptionsAn AttributeError naming NoneType is about the object, not the attribute.

The next lesson covers the third kind — semantic errors, which produce no message at all — and the strategies for a program that runs perfectly and does the wrong thing.

Think Python, 2nd edition — Allen B. Downey §A.1-A.2, pp. 193-197 — everything on these slides traces back here

Sources

  1. Think Python, 2nd edition — Allen B. Downey — Allen B. Downey, Think Python: How to Think Like a Computer Scientist, 2nd edition (Green Tea Press, 2015), §A.1-A.2, pp. 193-197
  2. Python documentation — Errors and Exceptions
  3. Python documentation — Using the Python Interpreter

Want this taught 1-on-1? Alexander tutors Python — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108