Capstone: Maze Rover + Reading Tracebacks

Lesson 8 of 8 in the Pre-COSMOS series, 39 slides, and the capstone. It snaps the whole series together into a dry run of camp's signature task: a Rover class from Lesson 2, driven by a control loop from Lesson 3 whose state is steered by an FSM next_state dictionary, also from Lesson 3, fed by a NumPy camera grid from Lesson 4 and a red color mask from Lesson 5, and paced by time.sleep, all to follow colored markers through a maze. The second half is explicit traceback practice: the LAST line names the error type and message, the line just above it points at the offending code, and the five errors you met across the series - a TypeError saying the object is not callable, a KeyError, an IndexError, an AttributeError, and a NameError - each map to one specific mistake. Three debugging traps show real tracebacks copied from CPython - a KeyError from a typo in a state name, a TypeError from an int that is not callable, and an out-of-bounds IndexError - and you find the culprit line and the fix. There are five checks and a scaffolded your-turn Maze Rover Simulator build that ends on "you're ready for camp." Every snippet was run on CPython 3.12 with numpy 2.3, and the printed output and traceback text were copied verbatim.

Subject: Python · 68 slides · code lesson

Open the interactive version of this deck · Homework for this lesson

What this lesson covers

The lesson, slide by slide

1. Capstone: the Maze Rover + Reading Tracebacks

Title

Pre-COSMOS · Lesson 8 of 8

Everything you learned - the class, the loop, the FSM, the NumPy camera, the red mask - assembled into camp's signature task. Plus the skill you'll use most: reading the error and finding the line.

2. What you will be able to do

Objectives

This is the last lesson before camp. We assemble all eight lessons into one running robot, then practice the thing you'll spend half your camp time on: figuring out why won't this run. By the end you can:

3. What survived from while Loops as Robot Control Loops?

Warm-up

Discussion prompt

Before we open Capstone: Maze Rover + Reading Tracebacks: without looking back, what was the main idea of while Loops as Robot Control Loops, and what could you do by the end of it that you could not do before?

Hint: One sentence for the idea, one for the skill. If the second one is blank, that is the part to revisit.

Answer:

Day 7 maze upgrade (34 slides), build-it-yourself: while loops as a robot control loop - sense, decide, act, update, stop - around 'what makes this robot stop?'. Students BUILD the controller themselves in four 'your turn' levels (skeleton + target behavior, no answer); the teacher demo shows only the target behavior; debug challenges hide the body; and the complete solution is revealed on one slide at the very end.

4. Eight lessons, one robot

Concept

Figure (svg): Six labeled boxes feeding into one rover: a Rover class, a control loop, an FSM dict, a NumPy camera grid, a red mask, and time.sleep, all arrows pointing into a central rover box.

Each lesson was one piece. Today they snap together.

Every lesson built one part of the same machine. None of them was the whole robot - until now.

Camp's signature task: send a rover through a maze, following colored markers. That needs all six pieces working together.

5. Break it if you can: Eight lessons, one robot

Counterexample

Discussion prompt

Every lesson built one part of the same machine. None of them was the whole robot - until now.

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

Answer:

Camp's signature task: send a rover through a maze, following colored markers. That needs all six pieces working together.

6. Today's roadmap

Concept

Two halves: assemble it, then debug it.

Assemble
Six pieces -> one maze rover.
The FSM
scan -> steer -> drive, repeat.
Detect & steer
Red in the camera -> turn.
Tracebacks
Read the error, find the line.

7. Which is which: Today's roadmap

Matching

Match the pairs

From Today's roadmap — match each one to what it actually does. The descriptions have been shuffled.

  • c1. Assemble
  • c2. The FSM
  • c3. Detect & steer
  • c4. Tracebacks
  • b1. Six pieces -> one maze rover.
  • b2. scan -> steer -> drive, repeat.
  • b3. Red in the camera -> turn.
  • b4. Read the error, find the line.

Why: Assemble, The FSM, Detect & steer, Tracebacks are easy to tell apart while they are sitting next to their descriptions and much harder afterwards, which is what this checks.

8. Assembling the Pieces

Section

Section 1

9. The six pieces, by lesson

Concept

Here's the parts list. You've met every one - the capstone is just wiring, not new ideas.

10. A loop with a tiny brain

Intuition

Think of the loop as the rover's heartbeat - one beat per step. On each beat it asks one question: what state am I in, and what do I do?

The FSM dict is the tiny brain answering 'what next.' The camera + red mask are the eyes. The Rover object is the body that actually moves. Same loop you've written a dozen times - it just has senses now.

11. By analogy: A loop with a tiny brain

Analogy

Discussion prompt

Explain A loop with a tiny brain by analogy to something with no Python in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

Think of the loop as the rover's heartbeat - one beat per step. On each beat it asks one question: what state am I in, and what do I do?

12. The FSM brain: next_state

Worked example

The whole 'brain' is one dict. Each state maps to the state that follows it. Cycle it and you get the rover's rhythm.

next_state = {"scan": "steer", "steer": "drive", "drive": "scan"}
state = "scan"
for i in range(6):
    print(state)
    state = next_state[state]

state = next_state[state] looks up where to go next, then the loop repeats. Watch the state walk the cycle.

istate (printed)next_state[state]
0scansteer
1steerdrive
2drivescan
3scansteer
4steerdrive
5drivescan

Real output (6 prints): scan steer drive scan steer drive - the cycle repeats forever, which is exactly what a control loop should do.

13. Fill in: state (printed) for The FSM brain: next_state

Comparison

Comparison matrix

From The FSM brain: next_state: refill the state (printed) column from what you know. The rest of the table is as it appeared.

istate (printed)next_state[state]
0scansteer
1steerdrive
2drivescan
3scansteer
4steerdrive
5drivescan

14. The eyes: a camera frame + red count

Worked example

The camera is a NumPy grid: 1 where the marker is red, 0 elsewhere. frame == 1 builds the red mask; .sum() counts the red pixels.

import numpy as np
frame = np.array([[0, 0, 1, 0],
                  [0, 1, 1, 0],
                  [0, 0, 0, 0],
                  [1, 0, 0, 0]])
def red_count(frame):
    return int((frame == 1).sum())
print(red_count(frame))

(frame == 1) is a True/False mask; summing it counts the Trues - one per red pixel.

rowvaluesreds in row
00 0 1 01
10 1 1 02
20 0 0 00
31 0 0 01

Total reds = 1 + 2 + 0 + 1 = 4. Real output: 4.

15. What each one costs: The eyes: a camera frame + red count

Trade off

Comparison matrix

From The eyes: a camera frame + red count: every row here is a choice with a cost. Fill the values column, then say which row you would actually pick and what you give up for it.

rowvaluesreds in row
00 0 1 01
10 1 1 02
20 0 0 00
31 0 0 01

16. The pacing: time.sleep

Worked example

A real robot loop would spin too fast for a human to watch. time.sleep(seconds) pauses each step so you can see the rover think. (We use 0.0 here so the trace is instant.)

import time
for tick in range(3):
    print(f"tick {tick}")
    time.sleep(0.0)
print("done")

time.sleep just waits, then the loop continues - it changes the pace, never the result.

tickreal output
0tick 0
1tick 1
2tick 2
after loopdone

17. Watch it run: The pacing: time.sleep

Pattern

Step through it

Step through The pacing: time.sleep one row at a time. What is driving the change, and what would the row after the last one be?

  1. Step 1: tick is 0
  2. Step 2: tick is 1
  3. Step 3: tick is 2
  4. Step 4: tick is after loop

18. Wiring it: detect red -> steer toward it

Concept

The connection that makes it a maze follower: when the scan state sees red, the steer state turns the rover toward the marker. No red -> hold course.

perception-action loop — The core robot pattern: sense the world (camera), decide (FSM + red detection), act (move the rover) - then repeat. Every autonomous robot is some version of this.

19. Teach it back: Wiring it: detect red -> steer toward it

Explain it

Discussion prompt

Explain Wiring it: detect red -> steer toward it to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

The connection that makes it a maze follower: when the scan state sees red, the steer state turns the rover toward the marker. No red -> hold course.

20. The assembled capstone (dry run)

Worked example

All six pieces in one program: the Rover, the loop, the next_state FSM, a sequence of camera frames, the red_count mask. It runs the perception-action loop for 6 steps.

rex = Rover("Rex")
state = "scan"
for i in range(6):
    frame = frames[i % len(frames)]
    if state == "scan":
        print(f"[scan] red pixels seen: {red_count(frame)}")
    elif state == "steer":
        if red_count(frame) > 0:
            print("[steer] red detected - turning toward it")
        else:
            print("[steer] no red - holding course")
    elif state == "drive":
        rex.move()
    state = next_state[state]

Each pass: read the frame, act on the state, then advance the FSM. drive calls rex.move(), which spends 10 energy.

istatereal printed output
0scan[scan] red pixels seen: 0
1steer[steer] red detected - turning toward it
2driveRex rolls forward. Energy: 90
3scan[scan] red pixels seen: 0
4steer[steer] red detected - turning toward it
5driveRex rolls forward. Energy: 80

That's the capstone running. Same six lines of output every time - copied verbatim from CPython 3.12.

21. Following the marker through the maze

Intuition

Stretch this 6-step run across a real maze: every scan is a glance at the camera, every steer is a turn toward the red marker the glance found, every drive is a roll forward. Repeat, and the rover threads the maze marker by marker.

Nothing here is new - it's the same six pieces looping. A longer maze is just more turns of the same loop, with different camera frames coming in.

22. Rebuild the recipe: The capstone recipe

Ranking

Put in order

These are the steps of The capstone recipe, scrambled. Put them back in order before the next slide shows you.

  1. Make the object that acts: rover = Rover(name).
  2. Define the FSM as a dict: next_state = {...}.
  3. Loop: on each step, read the camera frame (NumPy).
  4. Detect with a mask: red_count(frame) over the frame.
  5. Decide & act on the current state (steer toward red, drive, scan).
  6. Advance: state = next_state[state], then repeat.

Why: This is the order the recipe itself gives. Recalling the sequence without the slide in front of you is the difference between recognising the method and being able to run it — most of what goes wrong in practice is a step done out of turn.

23. The capstone recipe

Pattern

Any sense-decide-act robot you build at camp follows this shape:

  1. Make the object that acts: rover = Rover(name).
  2. Define the FSM as a dict: next_state = {...}.
  3. Loop: on each step, read the camera frame (NumPy).
  4. Detect with a mask: red_count(frame) over the frame.
  5. Decide & act on the current state (steer toward red, drive, scan).
  6. Advance: state = next_state[state], then repeat.

24. Where does it stop working: The capstone recipe

Edge cases

Discussion prompt

The capstone recipe works on the cases you have just seen. Push it to the edge: what is the most degenerate input it still handles — empty, zero, one item, everything equal — and what is the first case where it stops being true? Name the case, not just "it breaks".

Hint: Try the smallest legal input, then the largest, then the one where two things collide. Methods are specified at their edges; the middle takes care of itself.

Answer:

Any sense-decide-act robot you build at camp follows this shape:

25. Rule out three: Check: which pieces?

Elimination

Eliminate the wrong options

The capstone maze rover combines which set of pieces?

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. A Rover class, a control loop, an FSM next_state dict, a NumPy camera grid, a red mask, and time.sleep
  • B. Only a Rover class and a for loop
  • C. A NumPy grid and a list of strings, nothing else
  • D. A web server, a database, and a GUI

Survives elimination: A

Why: The capstone wires together all six pieces from the series: the Rover object (L2), the control loop (L3), the FSM next_state dict (L3), the NumPy camera grid (L4), the red color mask (L5), and time.sleep for pacing.

26. Check: which pieces?

Check

Recall the parts list before answering.

Check your understanding

The capstone maze rover combines which set of pieces?

  • A. A Rover class, a control loop, an FSM next_state dict, a NumPy camera grid, a red mask, and time.sleep (correct)
  • B. Only a Rover class and a for loop
  • C. A NumPy grid and a list of strings, nothing else
  • D. A web server, a database, and a GUI

Answer: A

Why: The capstone wires together all six pieces from the series: the Rover object (L2), the control loop (L3), the FSM next_state dict (L3), the NumPy camera grid (L4), the red color mask (L5), and time.sleep for pacing.

Why B tempts people
The class and loop are two of the pieces, but without the FSM dict, the camera grid, and the red mask the rover can't sense markers or decide what to do.
Why C tempts people
A grid and strings are part of the camera and states, but this leaves out the Rover object, the loop, the FSM, and the detection that make it act.
Why D tempts people
Those are pieces of a different kind of program. This camp project is a pure-Python robotics dry run, not a web app.

27. Answer it before you see the options: Check: the FSM order

Prediction

Predict first

Starting at "scan", what are the first four states the loop visits?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: scan, steer, drive, scan

Why: Each step looks up state = next_state[state]: scan -> steer -> drive -> scan. After three steps the cycle wraps back to scan, so the fourth state is scan again.

28. Check: the FSM order

Check

next_state = {"scan": "steer", "steer": "drive", "drive": "scan"}. Start at scan.

Check your understanding

Starting at "scan", what are the first four states the loop visits?

  • A. scan, steer, drive, scan (correct)
  • B. scan, drive, steer, scan
  • C. scan, steer, scan, steer
  • D. scan, scan, scan, scan

Answer: A

Why: Each step looks up state = next_state[state]: scan -> steer -> drive -> scan. After three steps the cycle wraps back to scan, so the fourth state is scan again.

Why B tempts people
Swaps steer and drive. The dict maps scan to steer (not drive), so steer comes second and drive third.
Why C tempts people
Skips drive entirely. scan maps to steer and steer maps to drive, so drive must appear before returning to scan.
Why D tempts people
Assumes the state never changes, but state = next_state[state] reassigns it every pass, so it walks the cycle.

29. Check: how red drives the steer

Check

In the steer state the rover checks red_count(frame).

Check your understanding

In the steer state, what makes the rover decide to turn toward the marker?

  • A. red_count(frame) > 0 - there is at least one red pixel in the camera frame (correct)
  • B. The frame is exactly all zeros
  • C. The state string equals "drive"
  • D. rover.energy drops below 50

Answer: A

Why: The steer branch turns toward the marker when red_count(frame) > 0 - meaning the red mask found at least one red pixel. No red pixels means hold course instead.

Why B tempts people
An all-zero frame has red_count 0, which is the 'no red - hold course' case, the opposite of turning toward a marker.
Why C tempts people
drive is a different state that calls move(); the turn decision happens in steer, based on red, not on the state name.
Why D tempts people
Energy tracks the rover's battery; it isn't what the steer decision reads. The decision reads the red pixel count.

30. Reading Tracebacks

Section

Section 2

31. Why won't this run?

Concept

At camp you'll hit errors constantly - everyone does. The skill that separates 'stuck for an hour' from 'fixed in a minute' is reading the traceback instead of panicking at the red text.

A traceback is not noise. It's Python telling you exactly what broke and exactly where. You just have to know which lines to read.

32. Picture it first: Read a traceback bottom-up

Picture it

Figure (svg): A traceback block with an arrow pointing at the second-to-last line labeled the culprit line, and an arrow at the last line labeled what went wrong.

Last line = what. Line above = where.

Discussion prompt

Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.

Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.

Answer:

Tracebacks read like a receipt printed in reverse. Start at the bottom: the last line is the headline - what went wrong (the error type and message).

33. Read a traceback bottom-up

Intuition

Tracebacks read like a receipt printed in reverse. Start at the bottom: the last line is the headline - what went wrong (the error type and message).

Then look one line up from there: that's the actual line of your code that triggered it - where it went wrong. Those two lines answer 95% of 'why won't this run.'

Figure (svg): A traceback block with an arrow pointing at the second-to-last line labeled the culprit line, and an arrow at the last line labeled what went wrong.

Last line = what. Line above = where.

34. Teach it back: Read a traceback bottom-up

Explain it

Discussion prompt

Explain Read a traceback bottom-up to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.

Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.

Answer:

Tracebacks read like a receipt printed in reverse. Start at the bottom: the last line is the headline - what went wrong (the error type and message).

35. Anatomy of a real traceback

Worked example

Here is a real traceback from CPython 3.12, copied verbatim. Let's label every part.

Traceback (most recent call last):
  File "trap_a.py", line 5, in <module>
    state = next_state["drve"]
            ~~~~~~~~~~^^^^^^^^
KeyError: 'drve'

Line 5 (the ^^^ carets) marks the exact spot; the last line names the error. Read those two and you know the bug.

line in tracebackwhat it tells you
Traceback (most recent...)an error happened; details follow
File "...", line 5the file and line number
state = next_state["drve"]the offending code (the culprit line)
~~~^^^ caretspoints under the exact bad part
KeyError: 'drve'the LAST line: the error type + message

36. Something is wrong here: Debug 1: a state-typo KeyError

Anomaly

Predict first

A student writes this, and it looks reasonable:

You ran the loop and got red text. The program prints scan once, then dies.

It is wrong. Say what breaks — and say it before you turn the page.

Correct: You meant drive but typed drve.

Read the last line, then look up one line, then fix the key.

Why: You meant drive but typed drve. The dict has no key 'drve', so the lookup fails.

37. Debug 1: a state-typo KeyError

Trap

The trap

You ran the loop and got red text. The program prints scan once, then dies.

The code: state = next_state["drve"]

Why: You meant drive but typed drve. The dict has no key 'drve', so the lookup fails.

Real traceback ends in KeyError: 'drve', pointing at line 5

Why: The last line names the error (KeyError) and the missing key ('drve'); the line above it is the lookup that failed.

The fix

Read the last line, then look up one line, then fix the key.

KeyError: 'drve' -> a key that isn't in the dict

Why: A KeyError always means: you asked a dict for a key it doesn't have. The message even quotes the bad key.

Fix: state = next_state[state] (no typo'd literal)

Why: Use the real state variable - or spell drive correctly. The keys are exactly scan, steer, drive.

38. Something is wrong here: Debug 2: 'int' object is not callable

Anomaly

Predict first

A student writes this, and it looks reasonable:

You wanted the rover's battery level, so you wrote rex.energy().

It is wrong. Say what breaks — and say it before you turn the page.

Correct: energy is a number (an int), not a method.

Drop the parentheses - data is read with no ().

Why: energy is a number (an int), not a method. The () tells Python to call the number 90 - which makes no sense.

39. Debug 2: 'int' object is not callable

Trap

The trap

You wanted the rover's battery level, so you wrote rex.energy().

The code: print(rex.energy())

Why: energy is a number (an int), not a method. The () tells Python to call the number 90 - which makes no sense.

Real traceback ends in TypeError: 'int' object is not callable, pointing at the rex.energy() line

Why: The carets sit under rex.energy(). 'object is not callable' = you put () on something that isn't a function.

The fix

Drop the parentheses - data is read with no ().

Fix: print(rex.energy)

Why: energy is an attribute, so you read it straight, no parentheses. Actions get (); data does not.

Rule of thumb for 'X object is not callable'

Why: You added () to a value (an int, list, str...). Remove the (), or you meant a different name that really is a function.

40. Break it on purpose: Debug 2: 'int' object is not callable

Break the constraint

Discussion prompt

The rule this trap just fixed:

energy is an attribute, so you read it straight, no parentheses. Actions get (); data does not.

Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?

Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.

Answer:

energy is a number (an int), not a method. The () tells Python to call the number 90 - which makes no sense.

41. Something is wrong here: Debug 3: an IndexError off the grid

Anomaly

Predict first

A student writes this, and it looks reasonable:

Your camera frame has 2 rows, but you reach for frame[5].

It is wrong. Say what breaks — and say it before you turn the page.

Correct: Valid row indexes are 0 and 1. Index 5 is past the end of the array's first axis.

Index within range - check the shape first.

Why: Valid row indexes are 0 and 1. Index 5 is past the end of the array's first axis.

42. Debug 3: an IndexError off the grid

Trap

The trap

Your camera frame has 2 rows, but you reach for frame[5].

The code: print(frame[5]) on a 2-row grid

Why: Valid row indexes are 0 and 1. Index 5 is past the end of the array's first axis.

Real traceback: IndexError: index 5 is out of bounds for axis 0 with size 2

Why: The last line spells it out: axis 0 (rows) only has size 2, so index 5 doesn't exist. NumPy tells you the size.

The fix

Index within range - check the shape first.

Fix: use a valid index like frame[0] or frame[1]

Why: An IndexError means the position is past the end. Print frame.shape to see how big it actually is.

Rule of thumb for IndexError

Why: You asked for an item beyond the last one. Indexes start at 0 and stop at length minus 1.

43. Which of these survive contact with Capstone: Maze Rover + Reading Tracebacks?

Two truths and a lie

Sort into buckets

Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.

Holds up
Every lesson built one part of the same machine. None of them was the whole robot - until now.; Two halves: assemble it, then debug it.; Think of the loop as the rover's heartbeat - one beat per step. On each beat it asks one question: what state am I in, and what do I do?
Breaks
You ran the loop and got red text. The program prints scan once, then dies.; You wanted the rover's battery level, so you wrote rex.energy().
sound
These are stated as this lesson states them — each one survives the edge cases Capstone: Maze Rover + Reading Tracebacks puts it through.
flawed
Each of these is lifted from a trap in this deck: reasonable-sounding, and wrong in a way that only shows up once you rely on it.

44. Two more: AttributeError & NameError

Worked example

The last two errors of the series, both real CPython 3.12 tracebacks. rex.faceing is a typo for facing; using Rover before it's defined or imported is a NameError.

rex = Rover("Rex")
print(rex.faceing)
# ---
# (in a fresh file, before defining Rover)
# rex = Rover("Rex")

Both last lines name the error AND hint the fix - 3.12 even suggests 'Did you mean: ...'.

mistakelast line of the real traceback
rex.faceing (typo)AttributeError: 'Rover' object has no attribute 'faceing'. Did you mean: 'facing'?
Rover(...) before it existsNameError: name 'Rover' is not defined

AttributeError -> check the .name spelling and that the object really has it. NameError -> you forgot to define it or import it (or typed it wrong).

45. The five errors, each a specific mistake

Concept

Across this whole series you met exactly five errors. Each one maps to one kind of mistake. Memorize this table and you can fix most camp bugs on sight.

error (last line)what it meansthe mistake
TypeError: 'int' object is not callableyou called a non-functionadded () to data: rover.energy()
KeyError: 'drve'dict has no such keywrong/typo'd dict key: next_state["drve"]
IndexError: ... out of boundsposition past the endindex too big: frame[5] on 2 rows
AttributeError: ... no attribute 'speed'object lacks that namewrong attribute: rover.speed
NameError: name 'Rover' is not definedname was never createdused a name before defining/importing it

46. By analogy: The five errors, each a specific mistake

Analogy

Discussion prompt

Explain The five errors, each a specific mistake by analogy to something with no Python in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.

Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.

Answer:

Across this whole series you met exactly five errors. Each one maps to one kind of mistake. Memorize this table and you can fix most camp bugs on sight.

47. The error word tells you where to look

Intuition

You don't have to memorize fixes - the error type points you at the kind of thing to check:

48. Break it if you can: The error word tells you where to look

Counterexample

Discussion prompt

You don't have to memorize fixes - the error type points you at the kind of thing to check:

That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.

Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.

49. How sure are you: Check: which line names the error?

Commit first

Predict first

In a Python traceback, which line tells you the error type and message?

Commit to an answer, then rate it — certain, fairly sure, or guessing — and write the rating down before you turn the page.

Correct: The very last line

Why: The last line of a traceback names the error type and message, like KeyError: 'drve' or TypeError: 'int' object is not callable. Read it first, then look one line up for the culprit code.

The rating matters as much as the answer: confident-and-wrong is the combination that survives revision, because nothing about it feels like it needs revisiting.

50. Check: which line names the error?

Check

Picture a traceback on your screen. Where do you look first?

Check your understanding

In a Python traceback, which line tells you the error type and message?

  • A. The very last line (correct)
  • B. The first line: 'Traceback (most recent call last):'
  • C. The line with the file name and line number
  • D. The carets (~~~^^^) line

Answer: A

Why: The last line of a traceback names the error type and message, like KeyError: 'drve' or TypeError: 'int' object is not callable. Read it first, then look one line up for the culprit code.

Why B tempts people
The first line just announces that an error occurred - it never names which error. The type and message are at the bottom.
Why C tempts people
The File line tells you WHERE (file and line number), not WHAT. The error type is on the final line.
Why D tempts people
The carets point under the exact bad part of the culprit line, but they don't name the error type - that's the last line's job.

51. Answer it before you see the options: Check: match the error to its cause

Prediction

Predict first

next_state = {"scan":"steer"} and rover.energy is 100. Which line raises a KeyError?

Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.

Correct: next_state["drive"]

Why: next_state only has the key "scan". Asking for "drive" - a key that isn't in the dict - raises KeyError: 'drive'. KeyError is always a missing dict key.

52. Check: match the error to its cause

Check

One of these lines runs cleanly; the rest each raise a different error. Read carefully.

Check your understanding

next_state = {"scan":"steer"} and rover.energy is 100.
Which line raises a KeyError?

  • A. next_state["drive"] (correct)
  • B. rover.energy()
  • C. rover.speed
  • D. frame[99]

Answer: A

Why: next_state only has the key "scan". Asking for "drive" - a key that isn't in the dict - raises KeyError: 'drive'. KeyError is always a missing dict key.

Why B tempts people
rover.energy() raises TypeError: 'int' object is not callable - you put () on a number. That's a TypeError, not a KeyError.
Why C tempts people
rover.speed raises AttributeError: 'Rover' object has no attribute 'speed' - a missing attribute, not a missing dict key.
Why D tempts people
frame[99] raises IndexError (out of bounds) - a list/array position past the end, which is an IndexError, not a KeyError.

53. Rule out three: Check: name that error

Elimination

Eliminate the wrong options

print(rex.speed) blows up. What is the last line of the traceback?

3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.

  • A. AttributeError: 'Rover' object has no attribute 'speed'
  • B. KeyError: 'speed'
  • C. TypeError: 'int' object is not callable
  • D. NameError: name 'speed' is not defined

Survives elimination: A

Why: rex is a real object but has no attribute named speed, so reading rex.speed raises AttributeError: 'Rover' object has no attribute 'speed'. AttributeError = a .name the object doesn't have.

54. Check: name that error

Check

rex is a Rover with name, energy, and facing - but no speed.

Check your understanding

print(rex.speed) blows up. What is the last line of the traceback?

  • A. AttributeError: 'Rover' object has no attribute 'speed' (correct)
  • B. KeyError: 'speed'
  • C. TypeError: 'int' object is not callable
  • D. NameError: name 'speed' is not defined

Answer: A

Why: rex is a real object but has no attribute named speed, so reading rex.speed raises AttributeError: 'Rover' object has no attribute 'speed'. AttributeError = a .name the object doesn't have.

Why B tempts people
KeyError is for missing DICT keys, like next_state['drve']. rex.speed is attribute access on an object, not a dict lookup.
Why C tempts people
TypeError: 'int' object is not callable comes from putting () on a number, like rex.energy(). There are no parentheses on rex.speed.
Why D tempts people
NameError is for a bare name Python never learned. Here rex IS defined and the dotted access is what fails, so it's an AttributeError, not a NameError.

55. Your Turn: Maze Rover Simulator

Section

Section 3 · build it yourself

56. The build: a maze rover dry run

Concept

Build the whole thing yourself: a robot that uses a class + control loop + FSM dict + NumPy camera + color detection + time.sleep to follow markers. Type every line, run after each one, and read your errors out loud - don't erase them.

#do thistools you'll use
1a camera frame + the red masknp.array, frame == 1, .sum()
2the FSM dict + the loopnext_state dict, for, state = next_state[state]
3wire detection -> steer decisionred_count(frame) > 0
4run it, then read one traceback you triggertime.sleep, the last line of the error

57. Fill in: tools you'll use for The build: a maze rover dry run

Comparison

Comparison matrix

From The build: a maze rover dry run: refill the tools you'll use column from what you know. The rest of the table is as it appeared.

#do thistools you'll use
1a camera frame + the red masknp.array, frame == 1, .sum()
2the FSM dict + the loopnext_state dict, for, state = next_state[state]
3wire detection -> steer decisionred_count(frame) > 0
4run it, then read one traceback you triggertime.sleep, the last line of the error

58. Milestone 1 — camera frame + red mask

Worked example

Your turn: make a small NumPy camera frame and count its red pixels. Predict the count before you run it.

Hint: build the mask with frame == 1, then .sum() it; wrap in int(...). You don't need a loop.

import numpy as np
frame = np.array([[0, 0, 1],
                  [0, 1, 0],
                  [0, 0, 0]])
red = (frame == 1)
print(red)
print(int(red.sum()))
linereal output
print(red)[[False False True]
[False True False]
[False False False]]
print(int(red.sum()))2

59. Milestone 2 — the FSM dict + loop

Worked example

Your turn: define next_state and step it 6 times from "scan". Predict the six states before running.

Hint: the engine is state = next_state[state] at the bottom of the loop; print state at the top.

next_state = {"scan": "steer", "steer": "drive", "drive": "scan"}
state = "scan"
for i in range(6):
    print(state)
    state = next_state[state]
ireal output
0scan
1steer
2drive
3scan
4steer
5drive

60. Watch it run: Milestone 2 — the FSM dict + loop

Pattern

Step through it

Step through Milestone 2 — the FSM dict + loop one row at a time. What is driving the change, and what would the row after the last one be?

  1. Step 1: i is 0
  2. Step 2: i is 1
  3. Step 3: i is 2
  4. Step 4: i is 3
  5. Step 5: i is 4
  6. Step 6: i is 5

61. Milestone 3 — wire detection -> steer

Worked example

Your turn: in the steer state, turn toward the marker only when there's red. Predict what an all-zero frame does before running.

Hint: the deciding test is red_count(frame) > 0; the else branch holds course.

def red_count(frame):
    return int((frame == 1).sum())

def steer(frame):
    if red_count(frame) > 0:
        print("[steer] red -> turn toward it")
    else:
        print("[steer] none -> hold")

steer(np.array([[0, 0, 1], [0, 1, 0]]))
steer(np.array([[0, 0, 0], [0, 0, 0]]))
callred_countreal output
steer(red frame)2[steer] red -> turn toward it
steer(empty frame)0[steer] none -> hold

62. Milestone 4 — run it & read a traceback

Worked example

Your turn: trigger one error on purpose, then read it. Set state = "scann" (a typo) and look it up. Predict the error type before running.

Hint: a missing dict key gives one specific error - look at the last line to name it, the line above for the culprit.

next_state = {"scan": "steer", "steer": "drive", "drive": "scan"}
state = "scann"
print(next_state[state])
traceback linereal text
File "...", line 3print(next_state[state])
last lineKeyError: 'scann'
the fixspell it "scan" - the key that exists

You read the error, named it (KeyError), found the line, and fixed it. That's the whole debugging loop.

63. What each one costs: Milestone 4 — run it & read a traceback

Trade off

Comparison matrix

From Milestone 4 — run it & read a traceback: every row here is a choice with a cost. Fill the real text column, then say which row you would actually pick and what you give up for it.

traceback linereal text
File "...", line 3print(next_state[state])
last lineKeyError: 'scann'
the fixspell it "scan" - the key that exists

64. Milestone 5 — the full simulator

Worked example

Your turn: assemble everything - Rover, the FSM, a list of camera frames, red_count, time.sleep, the loop. Predict the final energy before running.

import numpy as np, time
rex = Rover("Rex")
next_state = {"scan": "steer", "steer": "drive", "drive": "scan"}
frames = [np.array([[0,0,0],[0,0,0]]),
          np.array([[0,0,1],[0,1,0]]),
          np.array([[1,0,0],[0,0,0]])]
state = "scan"
for i in range(6):
    frame = frames[i % len(frames)]
    if state == "scan":
        print(f"[scan] saw {red_count(frame)} red")
    elif state == "steer":
        print("[steer] red -> turn" if red_count(frame) > 0 else "[steer] none -> hold")
    elif state == "drive":
        rex.move()
    time.sleep(0.0)
    state = next_state[state]
istatereal output
0scan[scan] saw 0 red
1steer[steer] red -> turn
2driveRex rolls forward. Energy: 90
3scan[scan] saw 0 red
4steer[steer] red -> turn
5driveRex rolls forward. Energy: 80

If yours ends with Rex rolls forward. Energy: 80 - you built the whole maze rover: class, loop, FSM, camera, detection, pacing, all working together.

65. Fill in: state for Milestone 5 — the full simulator

Comparison

Comparison matrix

From Milestone 5 — the full simulator: refill the state column from what you know. The rest of the table is as it appeared.

istatereal output
0scan[scan] saw 0 red
1steer[steer] red -> turn
2driveRex rolls forward. Energy: 90
3scan[scan] saw 0 red
4steer[steer] red -> turn
5driveRex rolls forward. Energy: 80

66. Show it off — you're ready for camp

Worked example

Explain your simulator out loud, piece by piece: point to the class, the FSM dict, the camera frame, the red mask, the loop, and the steer decision. Name what each one does.

Break it on purpose, then fix it from the traceback: typo a state (KeyError), put () on energy (TypeError), index off the grid (IndexError). Each time, read the last line, find the line above, fix it.

You can now assemble a robot from six independent pieces and debug it when it won't run. That's exactly what camp asks of you.

Eight lessons done. The dot, the class, the loop, the FSM, NumPy, color, decisions, and debugging are all yours now. You're ready for camp.

67. Connect it up: Capstone: Maze Rover + Reading Tracebacks

Connect it up

Draw it

One page, no notation unless you need it: draw how these connect — Assembling the Pieces · Reading Tracebacks · Your Turn: Maze Rover Simulator. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.

68. What you can do now

Recap

error you seewhat to check
TypeError: ... not callableyou put () on data (rover.energy())
KeyError: 'x'a dict key that doesn't exist
IndexError: out of boundsa list/array position past the end
AttributeError: no attributea .name the object doesn't have
NameError: not defineda name you never imported or defined

That's the whole Pre-COSMOS series. You went from 'what does the dot mean' to a debugged, sensing, deciding robot. Bring questions to camp - and have fun out there.

Sources

  1. Python 3 Tutorial - Errors and Exceptions (reading tracebacks, exception types)
  2. Python 3 - Built-in Exceptions (KeyError, IndexError, TypeError, AttributeError, NameError)
  3. All snippets executed on CPython 3.12; output copied verbatim. Author verification run, 2026-06-24 (Pre-COSMOS Lesson 8 of 8). — numpy 2.3 installed; printed output and full traceback text copied verbatim into the tables.

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

Book on Wyzant · Text (657) 465-8108