Skip to content

This textbook is in beta – content is actively being refined. Report issues or suggestions

4.7 Common Errors and Exceptions

Understanding Programming Errors

Programming errors are inevitable - even experienced programmers make them! The key is learning to recognize common error patterns, understand their causes, and know how to fix them quickly. This knowledge will make you a more efficient programmer and help you write better code from the start.

Types of Programming Errors

Syntax Errors

Syntax errors occur when your code doesn’t follow Python’s grammar rules. Python can’t even try to run code with syntax errors.

Common Syntax Errors and Fixes
# Error 1: Missing parentheses
print "Hello World"  # SyntaxError
# Fix:
print("Hello World")

# Error 2: Mismatched parentheses
print("Hello World"  # SyntaxError: missing closing parenthesis
# Fix:
print("Hello World")

# Error 3: Missing colon after if statement
if x > 5  # SyntaxError
    print("Big number")
# Fix:
if x > 5:
    print("Big number")

# Error 4: Incorrect indentation
def calculate_average(numbers):
total = sum(numbers)  # IndentationError: expected an indented block
return total / len(numbers)
# Fix:
def calculate_average(numbers):
    total = sum(numbers)
    return total / len(numbers)

# Error 5: Missing quotes
message = Hello World  # SyntaxError: invalid syntax
# Fix:
message = "Hello World"

# Error 6: Mismatched quotes
message = "Hello World'  # SyntaxError: unterminated string literal
# Fix:
message = "Hello World"
# or:
message = 'Hello World'
Reading Syntax Error Messages
## Example of syntax error message interpretation

def broken_function():
    if x > 5
        print("Greater than 5")

## Error message:

## File "example.py", line 2

##     if x > 5

##            ^

## SyntaxError: invalid syntax

## How to read this:

## 1. File name and line number (line 2)

## 2. The problematic line is shown

## 3. ^ points to where Python got confused

## 4. Error type: SyntaxError

## 5. Description: invalid syntax

## The fix: add missing colon

def fixed_function():
    if x > 5:
        print("Greater than 5")

Runtime Errors (Exceptions)

Runtime errors occur when Python tries to execute code that looks correct but can’t be completed for some reason.

Division by Zero
def safe_division_example():
    """Examples of division by zero errors and fixes."""

    ## Problem: Division by zero

    def calculate_average_broken(numbers):
        total = sum(numbers)
        count = len(numbers)
        return total / count  # ZeroDivisionError if numbers is empty

    ## Demonstration of the error

    print("=== Division by Zero Examples ===")

    try:
        result = calculate_average_broken([])  # This will crash
    except ZeroDivisionError as e:
        print(f"Error caught: {e}")
        print("Problem: Empty list causes division by zero")

    ## Fix 1: Check before dividing

    def calculate_average_fixed_v1(numbers):
        if len(numbers) == 0:
            return None  # or 0, or raise a meaningful error
        total = sum(numbers)
        count = len(numbers)
        return total / count

    ## Fix 2: Use try-except

    def calculate_average_fixed_v2(numbers):
        try:
            total = sum(numbers)
            count = len(numbers)
            return total / count
        except ZeroDivisionError:
            return None  # Handle the error gracefully

    ## Test the fixes

    test_cases = [[], [5], [1, 2, 3, 4, 5]]

    for numbers in test_cases:
        print(f"\nTesting with {numbers}:")
        print(f"  Fixed v1: {calculate_average_fixed_v1(numbers)}")
        print(f"  Fixed v2: {calculate_average_fixed_v2(numbers)}")

safe_division_example()
Index Out of Range
def index_error_examples():
    """Common index errors and how to avoid them."""

    print("=== Index Error Examples ===")

    # Problem: Accessing index that doesn't exist
    my_list = [1, 2, 3, 4, 5]
    print(f"List: {my_list}")
    print(f"List length: {len(my_list)}")
    print(f"Valid indices: 0 to {len(my_list) - 1}")

    # Common index errors:
    try:
        print(f"my_list[5]: {my_list[5]}")  # IndexError
    except IndexError as e:
        print(f"Error: {e}")
        print("Problem: Index 5 doesn't exist (valid indices: 0-4)")

    try:
        print(f"my_list[-6]: {my_list[-6]}")  # IndexError
    except IndexError as e:
        print(f"Error: {e}")
        print("Problem: Negative index -6 is too far back")

    # Fix 1: Check bounds before accessing
    def safe_get_element(lst, index):
        if 0 <= index < len(lst):
            return lst[index]
        else:
            return None  # or some default value

    # Fix 2: Use try-except
    def safe_get_element_v2(lst, index):
        try:
            return lst[index]
        except IndexError:
            return None

    # Test the fixes
    test_indices = [-1, 0, 2, 5, 10]
    print(f"\nTesting safe access methods:")

    for index in test_indices:
        safe_result = safe_get_element(my_list, index)
        safe_result_v2 = safe_get_element_v2(my_list, index)
        print(f"  Index {index}: {safe_result} | {safe_result_v2}")

index_error_examples()
Key Errors with Dictionaries
def key_error_examples():
    """Common dictionary key errors and solutions."""

    print("=== Key Error Examples ===")

    student_grades = {
        "Alice": 92,
        "Bob": 87,
        "Charlie": 94
    }

    print(f"Student grades: {student_grades}")

    ## Problem: Accessing key that doesn't exist

    try:
        grade = student_grades["David"]  # KeyError
        print(f"David's grade: {grade}")
    except KeyError as e:
        print(f"Error: {e}")
        print("Problem: 'David' is not in the dictionary")

    ## Fix 1: Check if key exists first

    def get_grade_safe_v1(grades_dict, student_name):
        if student_name in grades_dict:
            return grades_dict[student_name]
        else:
            return None  # or "Not found", or 0

    ## Fix 2: Use .get() method with default

    def get_grade_safe_v2(grades_dict, student_name, default=None):
        return grades_dict.get(student_name, default)

    ## Fix 3: Use try-except

    def get_grade_safe_v3(grades_dict, student_name):
        try:
            return grades_dict[student_name]
        except KeyError:
            return None

    ## Test the fixes

    test_students = ["Alice", "Bob", "David", "Eve"]
    print(f"\nTesting safe dictionary access:")

    for student in test_students:
        v1_result = get_grade_safe_v1(student_grades, student)
        v2_result = get_grade_safe_v2(student_grades, student, "Not found")
        v3_result = get_grade_safe_v3(student_grades, student)
        print(f"  {student}: {v1_result} | {v2_result} | {v3_result}")

key_error_examples()
Type Errors
def type_error_examples():
    """Common type errors and how to prevent them."""

    print("=== Type Error Examples ===")

    # Problem 1: Wrong operation for type
    try:
        result = "5" + 3  # TypeError: can't concatenate str and int
    except TypeError as e:
        print(f"Error: {e}")
        print("Problem: Trying to add string and number")

        # Fixes:
        fix1 = int("5") + 3      # Convert string to int
        fix2 = "5" + str(3)      # Convert int to string
        print(f"Fix 1 (convert to int): {fix1}")
        print(f"Fix 2 (convert to string): {fix2}")

    # Problem 2: Calling method on wrong type
    try:
        number = 42
        result = number.append(5)  # TypeError: int has no method append
    except AttributeError as e:
        print(f"\nError: {e}")
        print("Problem: Trying to use list method on integer")

        # Fix: Use correct type
        my_list = [42]
        my_list.append(5)
        print(f"Fix: Use list instead: {my_list}")

    # Problem 3: Function expects different type
    def calculate_area(length, width):
        return length * width

    try:
        area = calculate_area("5", 3)  # This works but might not be intended
        print(f"\nCalculating area with string: {area}")
        print("Result: '555' (string multiplication, probably not intended)")
    except TypeError as e:
        print(f"Error: {e}")

    # Better version with type checking
    def calculate_area_safe(length, width):
        # Check types
        if not isinstance(length, (int, float)):
            raise TypeError(f"Length must be a number, got {type(length)}")
        if not isinstance(width, (int, float)):
            raise TypeError(f"Width must be a number, got {type(width)}")

        return length * width

    # Test the safe version
    try:
        area = calculate_area_safe("5", 3)
    except TypeError as e:
        print(f"Safe version error: {e}")

        # Fix: convert or use correct types
        area = calculate_area_safe(float("5"), 3)
        print(f"Fixed: {area}")

type_error_examples()

Logic Errors

Logic errors are the trickiest - your code runs without crashing but produces wrong results.

Off-by-One Errors
def off_by_one_examples():
    """Common off-by-one errors and corrections."""

    print("=== Off-by-One Error Examples ===")

    # Problem 1: Wrong range in loop
    def print_numbers_buggy(n):
        """Print numbers 1 to n - but has off-by-one error."""
        print("Buggy version (should print 1 to n):")
        for i in range(n):  # Bug: this goes 0 to n-1
            print(i, end=" ")
        print()

    def print_numbers_fixed(n):
        """Print numbers 1 to n - corrected version."""
        print("Fixed version:")
        for i in range(1, n + 1):  # Correct: 1 to n inclusive
            print(i, end=" ")
        print()

    # Test both versions
    n = 5
    print(f"Printing numbers 1 to {n}:")
    print_numbers_buggy(n)
    print_numbers_fixed(n)

    # Problem 2: Array indexing errors
    def get_last_element_buggy(lst):
        """Get last element - with off-by-one error."""
        if len(lst) > 0:
            return lst[len(lst)]  # Bug: this is one index too far
        return None

    def get_last_element_fixed(lst):
        """Get last element - corrected version."""
        if len(lst) > 0:
            return lst[len(lst) - 1]  # Correct: last valid index
        return None

    # Even better: use negative indexing
    def get_last_element_pythonic(lst):
        """Get last element - Python way."""
        if len(lst) > 0:
            return lst[-1]  # Pythonic: -1 means last element
        return None

    # Test all versions
    test_list = [10, 20, 30, 40, 50]
    print(f"\nGetting last element from {test_list}:")

    try:
        buggy_result = get_last_element_buggy(test_list)
        print(f"Buggy version: {buggy_result}")
    except IndexError:
        print("Buggy version: IndexError (as expected)")

    fixed_result = get_last_element_fixed(test_list)
    pythonic_result = get_last_element_pythonic(test_list)
    print(f"Fixed version: {fixed_result}")
    print(f"Pythonic version: {pythonic_result}")

off_by_one_examples()
Logic Errors in Conditions
def logic_condition_examples():
    """Common logic errors in conditional statements."""

    print("=== Logic Condition Examples ===")

    ## Problem 1: Wrong boolean logic

    def check_valid_age_buggy(age):
        """Check if age is valid (13-65) - with logic error."""

        ## Bug: using OR instead of AND

        if age >= 13 or age <= 65:  # This is always True!
            return True
        return False

    def check_valid_age_fixed(age):
        """Check if age is valid (13-65) - corrected."""

        ## Fix: use AND

        if age >= 13 and age <= 65:
            return True
        return False

    ## Even better: more Pythonic

    def check_valid_age_pythonic(age):
        """Check if age is valid (13-65) - Pythonic way."""
        return 13 <= age <= 65

    ## Test all versions

    test_ages = [5, 13, 25, 65, 70]
    print("Testing age validation (should be valid for 13-65):")

    for age in test_ages:
        buggy = check_valid_age_buggy(age)
        fixed = check_valid_age_fixed(age)
        pythonic = check_valid_age_pythonic(age)
        print(f"  Age {age}: Buggy={buggy}, Fixed={fixed}, Pythonic={pythonic}")

    ## Problem 2: Incorrect comparison

    def grade_letter_buggy(score):
        """Convert score to letter grade - with logic errors."""

        ## Bug: wrong comparison operators and order

        if score > 90:
            return "A"
        elif score > 80:
            return "B"
        elif score > 70:
            return "C"
        elif score > 60:
            return "D"
        else:
            return "F"

        ## Problem: score of exactly 90 gets "B" instead of "A"

    def grade_letter_fixed(score):
        """Convert score to letter grade - corrected."""

        ## Fix: use >= for inclusive boundaries

        if score >= 90:
            return "A"
        elif score >= 80:
            return "B"
        elif score >= 70:
            return "C"
        elif score >= 60:
            return "D"
        else:
            return "F"

    ## Test both versions

    test_scores = [85, 90, 95, 79, 80]
    print(f"\nTesting grade conversion:")

    for score in test_scores:
        buggy = grade_letter_buggy(score)
        fixed = grade_letter_fixed(score)
        print(f"  Score {score}: Buggy='{buggy}', Fixed='{fixed}'")

logic_condition_examples()
Infinite Loops
def infinite_loop_examples():
    """Examples of infinite loops and how to fix them."""

    print("=== Infinite Loop Examples ===")

    # Problem 1: Counter never reaches end condition
    def count_down_buggy():
        """Count down from 5 - but creates infinite loop."""
        count = 5
        print("Buggy countdown (would run forever):")
        # Bug: incrementing instead of decrementing
        # while count > 0:
        #     print(count)
        #     count += 1  # Bug: should be count -= 1
        print("(Code commented out to prevent infinite loop)")

    def count_down_fixed():
        """Count down from 5 - corrected version."""
        count = 5
        print("Fixed countdown:")
        while count > 0:
            print(count, end=" ")
            count -= 1  # Fix: decrement the counter
        print("Done!")

    count_down_buggy()
    count_down_fixed()

    # Problem 2: Condition never becomes false
    def find_number_buggy():
        """Find a number in list - potential infinite loop."""
        numbers = [1, 3, 5, 7, 9]
        target = 6  # This number is NOT in the list
        i = 0

        print(f"\nSearching for {target} in {numbers}")
        print("Buggy version (would run forever if target not found):")

        # Bug: no check for end of list
        # while numbers[i] != target:
        #     i += 1
        # This would crash with IndexError when i >= len(numbers)
        print("(Code commented out to prevent error)")

    def find_number_fixed():
        """Find a number in list - corrected version."""
        numbers = [1, 3, 5, 7, 9]
        target = 6
        i = 0

        print("Fixed version:")
        # Fix: check bounds AND condition
        while i < len(numbers) and numbers[i] != target:
            i += 1

        if i < len(numbers):
            print(f"Found {target} at index {i}")
        else:
            print(f"{target} not found in list")

    find_number_buggy()
    find_number_fixed()

    # Problem 3: Wrong update in loop
    def print_even_numbers_buggy():
        """Print even numbers 0-10 - with infinite loop bug."""
        print("\nBuggy even numbers (would run forever):")
        # Bug: condition and increment don't match
        # i = 0
        # while i <= 10:
        #     if i % 2 == 0:
        #         print(i)
        #     i += 3  # Bug: incrementing by 3, so condition never fails correctly
        print("(Code commented out to prevent infinite loop)")

    def print_even_numbers_fixed():
        """Print even numbers 0-10 - corrected version."""
        print("Fixed even numbers:")
        i = 0
        while i <= 10:
            if i % 2 == 0:
                print(i, end=" ")
            i += 1  # Fix: increment by 1, or use i += 2 and remove the if
        print()

        # Alternative fix: increment by 2 and start with even number
        print("Alternative fix:")
        i = 0
        while i <= 10:
            print(i, end=" ")
            i += 2  # Skip odd numbers entirely
        print()

    print_even_numbers_buggy()
    print_even_numbers_fixed()

# Note: We don't actually run the infinite loop examples to avoid hanging
print("Infinite loop examples defined (safe to demonstrate concepts)")

Common Error Patterns and Prevention

Variable Name Errors

def variable_name_errors():
    """Common variable naming mistakes."""

    print("=== Variable Name Error Examples ===")

    # Problem 1: Typos in variable names
    def calculate_total_buggy():
        # Bug: typo in variable name
        num1 = 10
        num2 = 20
        total = num1 + num2
        return totall  # NameError: name 'totall' is not defined

    def calculate_total_fixed():
        # Fix: spell variable names correctly
        num1 = 10
        num2 = 20
        total = num1 + num2
        return total

    try:
        result = calculate_total_buggy()
    except NameError as e:
        print(f"Error: {e}")
        print("Problem: Typo in variable name 'totall' instead of 'total'")

    result = calculate_total_fixed()
    print(f"Fixed version result: {result}")

    # Problem 2: Using variable before defining it
    def use_before_define_buggy():
        # Bug: using variable before it's defined
        try:
            print(f"The answer is: {answer}")  # NameError
            answer = 42
        except NameError as e:
            print(f"\nError: {e}")
            print("Problem: Used 'answer' before defining it")

    def use_before_define_fixed():
        # Fix: define variable before using it
        answer = 42
        print(f"The answer is: {answer}")

    use_before_define_buggy()
    use_before_define_fixed()

variable_name_errors()

Function Definition and Call Errors

def function_errors():
    """Common function-related errors."""

    print("=== Function Error Examples ===")

    # Problem 1: Calling function with wrong number of arguments
    def greet_person(first_name, last_name):
        return f"Hello, {first_name} {last_name}!"

    try:
        # Bug: missing required argument
        message = greet_person("John")  # TypeError
    except TypeError as e:
        print(f"Error: {e}")
        print("Problem: Function expects 2 arguments, got 1")

        # Fix: provide all required arguments
        message = greet_person("John", "Smith")
        print(f"Fixed: {message}")

    # Problem 2: Function doesn't return value when expected
    def calculate_area_buggy(length, width):
        """Calculate area but forget to return result."""
        area = length * width
        # Bug: forgot to return the result
        print(f"Area calculated: {area}")

    def calculate_area_fixed(length, width):
        """Calculate area and return result."""
        area = length * width
        print(f"Area calculated: {area}")
        return area  # Fix: return the result

    print(f"\nBuggy function result: {calculate_area_buggy(5, 3)}")  # Returns None
    print(f"Fixed function result: {calculate_area_fixed(5, 3)}")

    # Problem 3: Indentation errors in function definition
    def indentation_demo():
        """Show common indentation mistakes."""
        print("Common indentation mistakes:")
        print("1. Mixing tabs and spaces")
        print("2. Inconsistent indentation levels")
        print("3. Missing indentation after colon")

        # This would cause IndentationError:
        # def broken_function():
        # print("This line should be indented")

        # Correct version:
        def working_function():
            print("This line is properly indented")

        working_function()

    indentation_demo()

function_errors()

Debugging Strategies for Common Errors

Systematic Error Detection

def debugging_strategies():
    """Demonstrate systematic approaches to finding errors."""

    print("=== Debugging Strategies ===")

    # Example: Debugging a complex function
    def process_grades_with_bugs(student_scores):
        """Process student grades - contains multiple types of errors."""

        # Add debug prints to trace execution
        print(f"DEBUG: Input scores: {student_scores}")

        # Bug 1: Not handling empty input (will cause errors later)
        total_score = 0
        student_count = 0
        grade_counts = {"A": 0, "B": 0, "C": 0, "D": 0, "F": 0}

        for student_score in student_scores:
            print(f"DEBUG: Processing score: {student_score}")

            # Bug 2: Not validating score type/range
            total_score += student_score
            student_count += 1

            # Bug 3: Logic error in grade assignment
            if student_score > 90:  # Should be >= 90
                grade_counts["A"] += 1
            elif student_score > 80:  # Should be >= 80
                grade_counts["B"] += 1
            elif student_score > 70:  # Should be >= 70
                grade_counts["C"] += 1
            elif student_score > 60:  # Should be >= 60
                grade_counts["D"] += 1
            else:
                grade_counts["F"] += 1

        # Bug 4: Division by zero if no students
        average_score = total_score / student_count
        print(f"DEBUG: Average calculated: {average_score}")

        # Bug 5: Not returning the result
        result = {
            "average": average_score,
            "total_students": student_count,
            "grade_distribution": grade_counts
        }
        print(f"DEBUG: Final result: {result}")
        # Missing return statement!

    def process_grades_debugged(student_scores):
        """Process student grades - debugged version."""

        print(f"DEBUG: Input scores: {student_scores}")

        # Fix 1: Handle empty input
        if not student_scores:
            print("DEBUG: No scores provided")
            return {"error": "No student scores provided"}

        total_score = 0
        student_count = 0
        grade_counts = {"A": 0, "B": 0, "C": 0, "D": 0, "F": 0}

        for student_score in student_scores:
            print(f"DEBUG: Processing score: {student_score}")

            # Fix 2: Validate score
            if not isinstance(student_score, (int, float)) or student_score < 0 or student_score > 100:
                print(f"DEBUG: Invalid score {student_score}, skipping")
                continue

            total_score += student_score
            student_count += 1

            # Fix 3: Correct grade boundaries
            if student_score >= 90:
                grade_counts["A"] += 1
            elif student_score >= 80:
                grade_counts["B"] += 1
            elif student_score >= 70:
                grade_counts["C"] += 1
            elif student_score >= 60:
                grade_counts["D"] += 1
            else:
                grade_counts["F"] += 1

        # Fix 4: Handle case where all scores were invalid
        if student_count == 0:
            print("DEBUG: No valid scores found")
            return {"error": "No valid scores found"}

        average_score = total_score / student_count
        print(f"DEBUG: Average calculated: {average_score}")

        # Fix 5: Return the result
        result = {
            "average": round(average_score, 2),
            "total_students": student_count,
            "grade_distribution": grade_counts
        }
        print(f"DEBUG: Final result: {result}")
        return result

    # Test both versions
    test_scores = [85, 90, 78, 92, 88]

    print("Testing buggy version:")
    buggy_result = process_grades_with_bugs(test_scores)
    print(f"Buggy result: {buggy_result}")  # Will be None due to missing return

    print("\nTesting debugged version:")
    fixed_result = process_grades_debugged(test_scores)
    print(f"Fixed result: {fixed_result}")

    # Test edge cases
    print("\nTesting edge cases:")
    edge_cases = [
        [],  # Empty list
        [90, 80, 70, 60],  # Boundary scores
        [105, -5, "invalid", 85],  # Invalid data mixed with valid
    ]

    for case in edge_cases:
        print(f"\nTesting {case}:")
        result = process_grades_debugged(case)
        print(f"Result: {result}")

debugging_strategies()

Error Prevention Best Practices

Writing Defensive Code

def defensive_programming():
    """Examples of defensive programming techniques."""

    print("=== Defensive Programming Examples ===")

    # Technique 1: Input validation
    def divide_safely(a, b):
        """Divide two numbers with comprehensive input validation."""

        # Validate input types
        if not isinstance(a, (int, float)):
            raise TypeError(f"First argument must be a number, got {type(a)}")
        if not isinstance(b, (int, float)):
            raise TypeError(f"Second argument must be a number, got {type(b)}")

        # Validate business logic
        if b == 0:
            raise ValueError("Cannot divide by zero")

        return a / b

    # Technique 2: Assertions for debugging
    def calculate_discount(price, discount_percent):
        """Calculate discount with assertions."""

        # Use assertions to check assumptions during development
        assert isinstance(price, (int, float)), f"Price must be a number, got {type(price)}"
        assert price >= 0, f"Price must be non-negative, got {price}"
        assert isinstance(discount_percent, (int, float)), f"Discount must be a number"
        assert 0 <= discount_percent <= 100, f"Discount must be 0-100%, got {discount_percent}"

        discount_amount = price * (discount_percent / 100)
        final_price = price - discount_amount

        # Assert postconditions
        assert final_price >= 0, f"Final price should not be negative: {final_price}"

        return final_price

    # Technique 3: Early returns to reduce nesting
    def process_student_data(student):
        """Process student data with early returns."""

        # Early validation with early returns
        if not isinstance(student, dict):
            return {"error": "Student must be a dictionary"}

        if "name" not in student:
            return {"error": "Student must have a name"}

        if "scores" not in student:
            return {"error": "Student must have scores"}

        if not isinstance(student["scores"], list):
            return {"error": "Scores must be a list"}

        if len(student["scores"]) == 0:
            return {"error": "Student must have at least one score"}

        # Now we can safely process the data
        scores = student["scores"]
        average = sum(scores) / len(scores)

        return {
            "name": student["name"],
            "average": round(average, 2),
            "total_scores": len(scores)
        }

    # Test defensive programming
    print("Testing divide_safely:")
    test_cases = [(10, 2), (10, 0), (10, "invalid")]

    for a, b in test_cases:
        try:
            result = divide_safely(a, b)
            print(f"  {a} / {b} = {result}")
        except (TypeError, ValueError) as e:
            print(f"  {a} / {b} -> Error: {e}")

    print("\nTesting calculate_discount:")
    discount_cases = [(100, 10), (100, -5), (100, 150)]

    for price, discount in discount_cases:
        try:
            result = calculate_discount(price, discount)
            print(f"  ${price} with {discount}% discount = ${result}")
        except AssertionError as e:
            print(f"  ${price} with {discount}% -> Assertion Error: {e}")

    print("\nTesting process_student_data:")
    student_cases = [
        {"name": "Alice", "scores": [85, 90, 88]},
        {"name": "Bob"},  # Missing scores
        {"scores": [85, 90]},  # Missing name
        "not a dict",  # Wrong type
    ]

    for student in student_cases:
        result = process_student_data(student)
        print(f"  {student} -> {result}")

defensive_programming()

Summary

Understanding and preventing common errors is essential for effective programming:

Error Categories

  1. Syntax Errors: Code doesn’t follow Python grammar

  2. Missing colons, parentheses, quotes

  3. Incorrect indentation

  4. Quick fix: Read error messages carefully

  5. Runtime Errors: Code crashes during execution

  6. Division by zero, index out of range, key errors

  7. Type errors from wrong operations

  8. Fix: Add validation and error handling

  9. Logic Errors: Code runs but produces wrong results

  10. Off-by-one errors, wrong conditions

  11. Infinite loops, incorrect algorithms

  12. Fix: Trace through code, test thoroughly

Prevention Strategies

  • Write defensive code: Validate inputs and assumptions

  • Use meaningful variable names: Avoid typos and confusion

  • Test early and often: Catch errors before they spread

  • Read error messages carefully: They tell you exactly what’s wrong

  • Use debugging tools: Print statements, breakpoints, step through code

Key Takeaways

  • Errors are normal - even experts make them

  • Each error type has common patterns and fixes

  • Good error handling makes your programs more reliable

  • Prevention is better than fixing bugs later

Remember: Learning to recognize and fix common errors quickly is a superpower that will make you a much more effective programmer!