Python • Debugging and Testing

Common Python Errors — Recognize common Python errors (NameError, IndexError, KeyError, TypeError, ValueError)

Learn to read Python's error messages so you can fix bugs faster and write more reliable code.

Code braces with terminal and markup symbols representing Python programming.
Inside this lesson 10 sections 5 questions 5 figures

SECTION 1

Historical Context & Motivation

Every programmer, from beginners to professionals at major tech companies, encounters errors on a daily basis. Errors are not a sign that something is wrong with you as a coder — they are an essential part of the programming process. In the early days of computing, error messages were cryptic numeric codes that only engineers could decipher. Over time, language designers realized that human-readable error messages could dramatically speed up development. Python, created by Guido van Rossum, was designed from the start with clarity and readability in mind — and that philosophy extends to its error messages.

1950s • Early Error Codes

Early computers displayed numeric error codes like "ERROR 404" or cryptic hexadecimal addresses, requiring programmers to consult thick reference manuals.

1991 • Python 0.9 Released

Guido van Rossum released Python with a philosophy of readability. Python's exception system gave meaningful names to errors, such as NameError and TypeError.

2000 • Python 2.0 & Exception Hierarchy

Python 2.0 organized exceptions into a class hierarchy under BaseException, making it possible to catch groups of related errors with a single handler.

2020s • Enhanced Error Messages

Python 3.10 and later introduced dramatically improved error messages with suggestions like "Did you mean...?" and precise caret indicators pointing to the exact location of the mistake.

Understanding Python's error messages is one of the most practical skills you can develop. Instead of feeling stuck when your code fails, you'll learn to read the traceback — Python's detailed report of what went wrong — and use it as a roadmap to fix the issue. This lesson covers the five most common errors you'll encounter: NameError, IndexError, KeyError, TypeError, and ValueError.

SECTION 2

Core Principles of Python Errors

Before diving into specific error types, it helps to understand how Python handles errors in general. When Python encounters something it cannot execute, it raises an exception — a special object that contains information about what went wrong and where. If the exception is not caught by your code, Python halts the program and displays a traceback, which is a detailed report showing the sequence of function calls that led to the error. Learning to read tracebacks is like learning to read a map — it tells you exactly where you are and how you got there.

1

Exceptions Are Objects

Every error in Python is an instance of a class that inherits from the BaseException class. This means errors carry data — including a message string and the line number where the problem occurred.
2

Tracebacks Read Bottom-Up

The most important information is at the bottom of a traceback: the error type and its message. The lines above show the call stack — the path Python took through your code to reach the error.
3

Errors Have Specific Names

Python uses descriptive names like NameError, IndexError, and TypeError. The name itself often tells you the category of mistake, so you can quickly narrow down the cause.
4

Errors Can Be Caught

Using try/except blocks, you can anticipate errors and handle them gracefully instead of crashing. However, you should first understand the error before deciding how to catch it.
5

Syntax Errors vs. Runtime Errors

Syntax errors are caught before code runs (like misspelling a keyword). Runtime errors — our focus — happen while the code is executing, often due to unexpected data or logic issues.
✦

KEY TAKEAWAY

Think of Python error messages like a car's dashboard warning lights. A "check engine" light doesn't mean your car is ruined — it tells you something specific needs attention. Similarly, a NameError is different from a TypeError, just like an oil light is different from a tire-pressure warning. The error name is your first clue to diagnosing the problem.

SECTION 3

Anatomy of a Python Traceback

When Python encounters an error, it prints a traceback that contains several key pieces of information. The diagram below shows how to read a typical traceback, which is essential for debugging any Python program. Every traceback follows the same structure: it starts with the phrase "Traceback (most recent call last)" and ends with the error type and its message.

ANATOMY OF A PYTHON TRACEBACKTraceback (most recent call last): File "main.py", line 5, in <module> result = calculate(score) File "main.py", line 2, in calculate return scor + 10NameError: name 'scor' is not defined. Did you mean: 'score'?① HEADER② CALL STACK(file, line, function)③ ERROR TYPE+ message + suggestionTIP: Read bottom-up!Start at the error line,then trace upward.Debugging Checklist:✓ Read the error type (NameError, TypeError, etc.)✓ Read the message ("name 'scor' is not defined")✓ Check the line number and file name✓ Look at Python's suggestion if one is provided
This diagram shows the three main parts of a Python traceback: the header at the top, the call stack in the middle (showing file names and line numbers), and the error type with message at the bottom. Always start reading from the bottom to quickly identify the problem.

Notice how the traceback in the diagram shows a NameError caused by a simple typo — the programmer wrote scor instead of score. Python even suggests the correct name in newer versions. This kind of mistake is incredibly common, and being able to read the traceback lets you fix it in seconds rather than minutes.

SECTION 4

How Python's Exception System Works

Python organizes all of its errors into a class hierarchy, which means every error type is a specialized version of a more general error type. At the very top of this tree is BaseException. Below that is Exception, which is the parent of almost all errors you'll encounter in everyday coding. The five errors we're studying in this lesson all branch off from Exception. Understanding this hierarchy helps you see how Python categorizes mistakes.

The Exception Hierarchy (Simplified)

PYTHON EXCEPTION HIERARCHY (SIMPLIFIED)BaseExceptionExceptionLookupErrorNameErrorTypeErrorValueError...othersIndexErrorKeyErrorQUICK REFERENCE — THE FIVE ERRORSNameErrorUndefinedvariable orfunction nameprint(mesage)IndexErrorList indexout ofrangelst[10]KeyErrorDictionarykey doesnot existd["age"]TypeErrorWrong typeused in anoperation"5" + 3ValueErrorRight type,wrongcontentint("abc")
The exception hierarchy shows how Python organizes errors. IndexError and KeyError are both children of LookupError because they both involve looking up something that doesn't exist. The bottom row provides a quick-reference card for each of the five errors.

When Python runs a line that fails, it creates an exception object of the appropriate class, fills it with a descriptive message, and then "raises" it. If no try/except block is present to catch the exception, Python unwinds the call stack — going back through every function that was called — and finally prints the traceback you see in your terminal or IDE. The hierarchy matters because you can catch a parent class to handle multiple error types at once: catching LookupError would catch both IndexError and KeyError.

SECTION 5

Detailed Breakdown of Each Error Type

1. NameError

A NameError occurs when you use a variable or function name that Python doesn't recognize. This typically happens because of a typo, because you forgot to define the variable before using it, or because you defined it inside a function and tried to use it outside that function's scope. For example, writing print(mesage) when your variable is actually named message will trigger a NameError.

2. IndexError

An IndexError is raised when you try to access a list (or tuple or string) using an index that doesn't exist. Remember that Python uses zero-based indexing, so a list with three elements has valid indices 0, 1, and 2. If you try to access index 3, Python will raise an IndexError with the message "list index out of range." This is especially common in loops where the counter goes one step too far.

3. KeyError

A KeyError happens when you try to access a dictionary using a key that doesn't exist. For example, if you have student = {"name": "Alex"} and you write student["grade"], Python will raise a KeyError because there is no "grade" key. You can avoid this by using the .get() method, which returns None (or a default value you specify) instead of crashing.

4. TypeError

A TypeError is raised when an operation or function receives a value of the wrong type. A classic example is trying to add a string and an integer: "Score: " + 95 fails because Python doesn't know how to concatenate a string with a number. The fix is to convert the integer to a string first using str(95). TypeErrors also appear when you call a function with the wrong number of arguments.

5. ValueError

A ValueError occurs when a function receives a value of the correct type but the value is inappropriate. For instance, int("hello") raises a ValueError because while "hello" is a string (the correct type for int() to receive), its content cannot be converted to an integer. Similarly, int("3.14") also raises a ValueError because "3.14" is not a valid integer literal, even though it looks numeric.

Error TypeTriggerExample CodeQuick Fix
NameErrorUsing an undefined nameprint(mesage)Check spelling; ensure variable is defined before use
IndexErrorIndex exceeds list lengthcolors[5] (list has 3 items)Check len(); use valid index range
KeyErrorDictionary key does not existd["age"] (no "age" key)Use .get() or check with in
TypeErrorOperation on incompatible types"age: " + 17Convert types: str(17) or use f-strings
ValueErrorRight type, wrong contentint("abc")Validate input before converting; use try/except

Summary of the five common Python errors with triggers, examples, and fixes

SECTION 6

Worked Example — Debugging a Grade Calculator

Imagine you've written a program to calculate a student's letter grade from a numeric score. The code has several bugs — each producing a different error type. Let's walk through the debugging process step by step, just like you would in a real project.

Debugging a Grade Calculator

  1. 1

    Step 1 — Run the Original Code

    Here is the buggy code: grades = {"A": 90, "B": 80, "C": 70} score = input("Enter score: ") adjusted = scor + 5 When you run this and enter "85", Python stops at line 3 with an error.
    Error: NameError: name 'scor' is not defined
  2. 2

    Step 2 — Fix the NameError (Typo)

    The traceback tells us that 'scor' is not defined. We see we misspelled score as scor. Fix it to adjusted = score + 5. Run again.
    Error: TypeError: can only concatenate str to str
  3. 3

    Step 3 — Fix the TypeError (String + Integer)

    The input() function always returns a string. So score is "85" (a string), and we're trying to add 5 (an integer) to it. We need to convert it first: score = int(input("Enter score: ")). But what happens if someone types "eighty-five" instead of a number?
    If user enters "eighty-five": ValueError: invalid literal for int() with base 10: 'eighty-five'
  4. 4

    Step 4 — Handle the ValueError (Bad Input)

    We can wrap the input in a try/except block to catch invalid input gracefully: try: score = int(input("Enter score: ")) except ValueError: print("Please enter a whole number.") Now the program handles non-numeric input without crashing. With a valid number like 85, the adjusted score becomes 90.
    adjusted = 90 ✓
  5. 5

    Step 5 — Verify the Final Program Works

    The corrected program now handles three types of errors: the NameError from a typo, the TypeError from mixing types, and the ValueError from bad user input. Each fix was guided by reading the error message carefully and understanding what it meant. This is the core debugging workflow: run → read the error → understand the cause → fix → repeat.
    Program runs without errors for valid numeric input and handles invalid input gracefully.

SECTION 7

Prevention Strategies & Comparisons

Now that you can recognize each error type, the next step is learning strategies to prevent them in the first place. Some errors are best avoided through careful coding habits, while others require defensive programming techniques like input validation or safe access methods. The table below compares each error type with its most effective prevention strategy.

Error TypePrevention StrategyLimitation / Caveat
NameErrorUse an IDE with autocomplete and linting (e.g., VS Code with Pylint). Define variables before use and keep names consistent.Linters may miss dynamically created variables. Consistent naming conventions reduce typos but don't eliminate them.
IndexErrorAlways check len() before indexing. Use for-each loops (for item in list) instead of index-based loops when possible.Sometimes you genuinely need an index. Off-by-one errors remain common even with length checks.
KeyErrorUse .get(key, default) for safe access. Check membership with if key in dict before accessing.Using .get() can mask bugs if you silently ignore missing keys instead of investigating why they're missing.
TypeErrorUse f-strings for string formatting instead of concatenation. Check types with isinstance() when accepting mixed inputs.Overusing type checks makes code rigid. Python's philosophy encourages "duck typing" — testing behavior, not type.
ValueErrorValidate input before conversion. Use try/except around int(), float() calls. Use .isdigit() for basic checks..isdigit() won't work for negative numbers or decimals. A try/except is often the most reliable approach.

Prevention strategies and their limitations for each error type

🛡️

KEY TAKEAWAY

Think of error prevention like defensive driving. You don't just learn what each road sign means — you also learn to anticipate what other drivers might do. In code, "defensive programming" means anticipating what unexpected data might flow through your program and adding checks before it causes a crash. The .get() method, len() checks, and try/except blocks are your seatbelts.

SECTION 8

Connection to Advanced Error Handling

The five errors covered in this lesson are your everyday debugging companions, but Python's exception system goes much deeper. As you advance in computer science, you'll encounter concepts like custom exception classes, exception chaining, context managers, and logging. The table below shows how each beginner concept connects to its more advanced counterpart.

What You Learned TodayAdvanced VersionWhere You'll See It
Reading tracebacks to find errorsUsing the traceback module and logging to capture errors in productionWeb development, server-side programming
Catching errors with try/exceptCreating custom exception classes; raise and finally blocksAPI development, large codebases
Checking types with isinstance()Type hints (def func(x: int) -> str) and static type checkers like mypyProfessional Python, open-source projects
Validating input before conversionData validation libraries like Pydantic; unit testing with pytestData science, machine learning pipelines
Understanding the exception hierarchyObject-oriented programming and class inheritanceAP Computer Science, university CS courses

How today's concepts connect to advanced topics

As you continue your journey in programming, you'll find that the debugging mindset you're building now — read the error, understand the cause, fix the code — scales to every language and every project size. Professional developers at companies like Google and Netflix spend a significant portion of their time reading error messages, writing tests, and building systems that handle errors gracefully. The skills in this lesson are truly foundational.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain the difference between a TypeError and a ValueError in your own words. Give a one-line code example for each.
Show answer
A TypeError occurs when you perform an operation on the wrong type of data entirely — for example, "hello" + 5 tries to add a string to an integer, and Python doesn't know which operation you intended. A ValueError occurs when the type is correct but the actual value is inappropriate — for example, int("abc") passes a string to int() (which expects a string), but the string "abc" cannot be converted to an integer. Think of it this way: TypeError is like putting diesel fuel in a gasoline car (wrong type of fuel), while ValueError is like putting contaminated gasoline in a gasoline car (right type, but bad content).

Question 2: BASIC CALCULATION

What error does the following code produce, and what is the fix? fruits = ["apple", "banana", "cherry"] print(fruits[3])
Show answer
This code produces an IndexError: list index out of range. The list fruits has three elements at indices 0, 1, and 2. Index 3 does not exist because Python uses zero-based indexing. To access the last element, you could use fruits[2] or, more flexibly, fruits[-1], which always returns the last element regardless of the list's length.

Question 3: INTERMEDIATE

The following code is supposed to look up a student's grade and print it. It has two bugs that produce two different errors. Identify both errors (in the order they would appear when running the code) and fix them. students = {"Alice": 92, "Bob": 85} name = "Charlie" print("Grade: " + students[name])
Show answer
Bug 1: KeyError: 'Charlie' — "Charlie" is not a key in the students dictionary. Fix this by using students.get(name, "N/A") or by checking if name in students first. Bug 2 (hidden until Bug 1 is fixed, for a valid name like "Alice"): TypeError: can only concatenate str (not "int") to str — the grade is an integer (92), and you can't concatenate a string with an integer. Fix this by converting the grade: print("Grade: " + str(students[name])) or by using an f-string: print(f"Grade: {students[name]}").

Question 4: APPLIED

You're building a simple quiz app. The user is asked to enter the number of their answer choice (1, 2, or 3). Write a Python code snippet that: (a) prompts the user for input, (b) converts it to an integer, (c) uses it to look up the answer from a list choices = ["Paris", "London", "Berlin"], and (d) handles both ValueError and IndexError gracefully with try/except blocks.
Show answer
Here is a solution: choices = ["Paris", "London", "Berlin"] try: answer = int(input("Enter 1, 2, or 3: ")) print(f"You chose: {choices[answer - 1]}") except ValueError: print("Please enter a number.") except IndexError: print("Choice must be 1, 2, or 3.") The ValueError handler catches non-numeric input like "abc". The IndexError handler catches out-of-range numbers like 0 or 5. We use answer - 1 because user choices are 1-based but list indices are 0-based.

Question 5: CRITICAL THINKING

Consider the following code that processes a list of student records (dictionaries). It contains potential errors. Without running the code, trace through it and predict every possible error that could occur. Then rewrite the code so that it handles all errors gracefully and never crashes. records = [{"name": "Alice", "score": "95"}, {"name": "Bob"}, {"name": "Carol", "score": "not_a_number"}] for i in range(len(records) + 1): student = records[i] grade = int(student["score"]) print(student["name"] + " earned " + grade)
Show answer
This code has four potential errors. (1) IndexError: range(len(records) + 1) goes up to index 3, but the list only has indices 0–2. Fix: use range(len(records)) or simply for student in records. (2) KeyError: Bob's record has no "score" key. Fix: use student.get("score", "0"). (3) ValueError: Carol's score is "not_a_number", which cannot be converted to an int. Fix: wrap the conversion in try/except. (4) TypeError: the final print concatenates a string with the integer grade. Fix: use an f-string like print(f"{student['name']} earned {grade}"). A robust rewrite would loop with for student in records, use .get(), wrap the int conversion in try/except ValueError, and use an f-string for output.

SUMMARY

Lesson Summary

Summary

Python's error messages are designed to help you, not frustrate you. A NameError means you used a name that doesn't exist (check for typos). An IndexError means you tried to access a position beyond the length of a list. A KeyError means a dictionary key doesn't exist. A TypeError means you mixed incompatible types in an operation. And a ValueError means the type was correct but the actual value was unusable.

The key debugging workflow is: read the traceback from the bottom up, identify the error type and message, locate the line number, and apply the appropriate fix. Prevent errors proactively by using tools like .get() for dictionaries, len() checks for lists, f-strings for safe string formatting, and try/except blocks for handling unpredictable input. These skills form the foundation of effective debugging in any programming language.

Varsity Tutors • Python • Common Python Errors