Previous | Tutorial index | Next

Tutorial 7: Common Errors and Debugging Loops

Learning Objective

Identify, understand, and fix the most frequent mistakes made when writing loops. Develop a systematic debugging approach to become a more confident and efficient programmer.

1. The Debugging Mindset

Debugging is a skill, not a failure. Even experienced programmers spend most of their time debugging. The key is having a systematic approach:

  1. Read the error message: Python's error messages are surprisingly helpful. Read them carefully!
  2. Reproduce the bug: Can you make it happen consistently?
  3. Isolate the problem: Which part of the code is causing the issue? Use print statements to narrow it down.
  4. Fix the bug: Make the smallest possible change to fix it.
  5. Test the fix: Does it work? Did you break anything else?
  6. Learn from it: What caused the bug? How can you avoid it in the future?

Remember: The goal isn't to write perfect code on the first try. The goal is to write code that you can fix efficiently when things go wrong.

2. Error Type 1: The "Off-by-One" Error

This is arguably the most common loop error. It happens when a loop runs one too many or one too few times.

Scenario A: Using range(stop) incorrectly

❌ WRONG - Stops before reaching 10:

for i in range(1, 10): print(i) # Prints 1, 2, 3, 4, 5, 6, 7, 8, 9 -- MISSES 10!

✅ RIGHT - Includes 10:

for i in range(1, 11): print(i) # Prints 1 through 10

Remember the Rule: range(start, stop) goes up to, but does NOT include, stop. Always think: "Do I want stop included? If yes, add 1!"

Scenario B: Using <= vs < in while loops

❌ WRONG - Runs one too many times:

i = 1 while i <= 5: print(i) # Prints 1, 2, 3, 4, 5 i += 1 print("Done") # THIS prints after i becomes 6!

If you intended to stop after printing 5, this is fine. But if you expected 6 iterations and only got 5, you have an off-by-one.

✅ RIGHT - If you want exactly 5 iterations (0, 1, 2, 3, 4):

i = 0 while i < 5: print(i) i += 1

Scenario C: Indexing lists

❌ WRONG - Index out of range:

fruits = ["apple", "banana", "cherry"] for i in range(len(fruits) + 1): # range(4) -> 0, 1, 2, 3 print(fruits[i]) # IndexError at i=3 (fruits[3] doesn't exist!)

✅ RIGHT - Loop through valid indices:

for i in range(len(fruits)): # range(3) -> 0, 1, 2 print(fruits[i])

Preventing Off-by-One Errors

3. Error Type 2: Infinite while Loops

An infinite loop is when the loop condition never becomes False. This freezes your program.

Scenario A: Forgetting to update the loop variable

❌ DANGEROUS - Infinite loop:

x = 5 while x > 0: print(x) # No update to x! x stays 5 forever.

✅ RIGHT - Update the variable:

x = 5 while x > 0: print(x) x -= 1 # x decreases: 5, 4, 3, 2, 1, 0 (then stops)

Scenario B: Updating in the wrong direction

❌ DANGEROUS - Infinite loop (going the wrong way):

x = 1 while x < 5: print(x) x -= 1 # x goes 0, -1, -2, -3... never reaches 5!

✅ RIGHT - Ensure the update moves toward the condition:

x = 1 while x < 5: print(x) x += 1 # x increases: 1, 2, 3, 4 (stops at 5)

Scenario C: Floating-point precision issues

❌ DANGEROUS - May never reach exactly 0:

x = 1.0 while x != 0.0: # Floating point arithmetic! May never equal exactly 0. print(x) x -= 0.1 # Might become 0.0000000000000001, never exactly 0!

✅ RIGHT - Use a tolerance or >/<:

x = 1.0 while x > 0.0: # Use > rather than != print(x) x -= 0.1 # Eventually becomes negative, stops

Detecting and Stopping Infinite Loops

  1. Keyboard Interrupt: Press Ctrl + C (or Cmd + C on Mac) to force-stop your program.

  2. Print Statements: Add print(f"x = {x}") at the top of the loop to see what's happening.

  3. Use a Debugger: Step through the code line by line to see the variables change.

  4. Timeouts (Advanced): You can add a counter to break after a certain number of iterations:

    counter = 0 while condition: counter += 1 if counter > 10000: print("Infinite loop detected! Breaking.") break # Rest of your code

4. Error Type 3: Mutating a List While Iterating

This is one of the most subtle and dangerous bugs. Modifying a list (adding or removing items) while iterating over it causes the loop to skip items or raise errors.

Scenario A: Removing items during iteration

❌ DANGEROUS - Skips items:

numbers = [1, 2, 3, 4, 5] for num in numbers: if num % 2 == 1: # If odd numbers.remove(num) # Remove it print(numbers) # Output: [2, 4] -- Wait, where did 3 and 5 go?

Why does this happen?

✅ FIX #1 - Iterate over a copy:

numbers = [1, 2, 3, 4, 5] for num in numbers[:]: # The [:] creates a COPY if num % 2 == 1: numbers.remove(num) print(numbers) # Output: [2, 4] -- All odds removed!

✅ FIX #2 - Build a new list (preferred):

numbers = [1, 2, 3, 4, 5] evens = [] for num in numbers: if num % 2 == 0: evens.append(num) numbers = evens print(numbers) # Output: [2, 4]

Scenario B: Adding items during iteration

❌ DANGEROUS - Infinite loop:

numbers = [1, 2, 3] for num in numbers: if num < 10: numbers.append(num * 2) # Keeps adding forever! # This will never end!

✅ FIX - Don't modify during iteration. If you must, iterate over a copy:

numbers = [1, 2, 3] new_numbers = numbers[:] # Copy for num in numbers: if num < 10: new_numbers.append(num * 2) numbers = new_numbers print(numbers) # Output: [1, 2, 3, 2, 4, 6]

Rule of Thumb: Never modify a list while iterating over it. Always iterate over a copy or build a new list.

5. Error Type 4: Indentation Errors

Python uses indentation to define code blocks. Incorrect indentation is the most common syntax error for beginners.

Scenario A: Forgetting to indent the loop body

❌ WRONG - IndentationError:

for i in range(3): print(i) # IndentationError: expected an indented block

✅ RIGHT:

for i in range(3): print(i) # Properly indented

Scenario B: Code that should be INSIDE the loop is OUTSIDE

❌ WRONG - Only runs ONCE (not in the loop):

total = 0 for i in range(1, 6): total += i print(f"Sum is: {total}") # This is NOT indented! Runs once.

Wait, this actually works in this case (it runs once after the loop). The real bug is when you want it to run multiple times:

❌ WRONG - Prints after the loop, not during:

for i in range(5): if i % 2 == 0: print(i) # This prints 0, 2, 4 (inside if, inside loop) print("Even numbers found!") # This prints ONCE after the loop!

If you want "Even numbers found!" printed every time you find an even number, it MUST be indented:

✅ RIGHT:

for i in range(5): if i % 2 == 0: print(i) print("Even numbers found!") # Now prints inside the if

Scenario C: Indenting the wrong block

❌ WRONG - else attached to wrong block:

for i in range(5): if i == 3: print("Found 3!") else: print(i) # This is correct. But what if we meant this? for i in range(5): if i == 3: print("Found 3!") else: print("3 not found") # This runs AFTER the loop! (Loop-else)

The else runs after the loop completes normally (no break). This is actually a valid Python feature, but it's easy to misinterpret.

Prevention: Use an editor with syntax highlighting and auto-indentation. Always use 4 spaces consistently (never mix tabs and spaces—use python -tt to check).

6. Error Type 5: Confusing break and continue

These are often used incorrectly, especially in while loops.

Scenario A: Using continue when you meant break

❌ WRONG - Creates an infinite loop:

x = 0 while x < 10: if x == 5: continue # We wanted to stop, but this just skips x=5 print(x) x += 1 # When x=5, continue jumps to the top without incrementing x. # x stays 5 forever => Infinite loop!

✅ RIGHT - Use break to exit:

x = 0 while x < 10: if x == 5: break # Exits the loop immediately print(x) x += 1

Scenario B: Using break when you meant continue

❌ WRONG - Exits the loop too early:

for i in range(1, 11): if i % 2 == 1: # If odd break # This exits the loop at i=1! We only print nothing! print(i) # Output: (nothing) -- loop stops at i=1

✅ RIGHT - Use continue to skip odds:

for i in range(1, 11): if i % 2 == 1: continue # Skip odd numbers print(i) # Output: 2, 4, 6, 8, 10

Scenario C: Using continue before updating the loop variable (in while)

❌ DANGEROUS - Infinite loop:

x = 0 while x < 5: if x == 2: continue # Jumps to top, x is STILL 2! print(x) x += 1 # Infinite loop at x=2

✅ RIGHT - Update BEFORE continue:

x = 0 while x < 5: if x == 2: x += 1 # Increment FIRST continue print(x) x += 1

Golden Rule for while + continue: If you use continue, ensure the loop condition will eventually become False. Update your variables before continue, not after!

7. Error Type 6: The "Empty Loop" Bug

Sometimes your loop condition is never True to begin with, so the loop body never runs.

Scenario: The loop condition is false from the start

❌ WRONG - Loop never runs:

i = 10 while i < 5: print(i) # Never runs because 10 < 5 is False i += 1 print("Done") # Prints "Done" immediately

✅ RIGHT - Ensure the condition is true:

i = 10 while i > 5: # Changed to > print(i) i -= 1 # Output: 10, 9, 8, 7, 6

Prevention: Always test your loop condition with the initial values. Add a print statement before the loop if you're not sure.

8. Error Type 7: Variable Scope and Loop Variable "Leakage"

In Python, loop variables do not have a separate scope. They remain defined after the loop ends.

for item in ["apple", "banana", "cherry"]: pass # Loop does nothing print(item) # Prints 'cherry'! The variable persists!

Why this matters: If you use the same variable name elsewhere, you might accidentally use the leftover value.

❌ WRONG - Using the loop variable after the loop accidentally:

for i in range(5): if i == 3: break print(i) # Prints 3 (the last value from the loop!) # If the loop runs to completion, i would be 4!

✅ RIGHT - If you need the value, explicitly set it:

found_index = -1 for i in range(5): if i == 3: found_index = i break print(found_index) # Explicitly tracked, no ambiguity

9. Debugging Tools and Techniques

A. Print Statements (The Beginner's Best Friend)

Add print statements to see what's happening inside your loop:

numbers = [1, 2, 3, 4, 5] total = 0 for num in numbers: print(f"DEBUG: num={num}, total before={total}") # Debug line total += num print(f"DEBUG: total after={total}") # Debug line print(f"Final total: {total}")

B. Using Python's pdb Debugger (Advanced)

You can step through your code line by line:

import pdb def buggy_function(): total = 0 for i in range(5): pdb.set_trace() # Execution pauses here total += i return total buggy_function()

Commands in pdb: n (next line), c (continue), p variable (print variable), q (quit).

C. Using the logging Module (Professional)

Instead of print, use logging for more control:

import logging logging.basicConfig(level=logging.DEBUG) for i in range(5): logging.debug(f"i = {i}")

D. Rubber Duck Debugging

Explain your code line by line to an inanimate object (like a rubber duck). Often, saying it out loud makes the bug obvious.

📝 Quiz: Check Your Understanding

Q1: What is the output of this code?

for i in range(1, 4): print(i)

a) 1 2 3
b) 1 2 3 4
c) 0 1 2 3

Answer(A)

Q2: What causes the infinite loop here?

x = 10 while x > 0: print(x)

a) x is never updated
b) The condition is wrong
c) Missing colon

Answer(A)

Q3: What is the output of this code?

numbers = [1, 2, 3, 4] for num in numbers: if num % 2 == 0: numbers.remove(num) print(numbers)

a) [1, 3]
b) [1, 3, 4]
c) [1, 2, 3, 4]

Answer(B) – skips 4 because list shifts.

Q4: (True/False) continue exits the loop immediately.

AnswerFalse – it skips current iteration.

Q5: What is the purpose of iterating over a copy of a list (list[:])? a) To improve performance
b) To safely modify the list while iterating
c) To reverse the list

Answer(B)

💻 In-Tutorial Coding Exercises

Exercise 1 (Fix the Off-by-One): The following code should print numbers from 5 to 15 (inclusive). Fix the bug.

for i in range(5, 15): print(i)
Solution
for i in range(5, 16): # Change 15 to 16 print(i)

Exercise 2 (Fix the Infinite Loop): Fix this code so it prints numbers from 10 down to 1 (inclusive).

x = 10 while x > 0: print(x) # Add missing line
SolutionAdd `x -= 1` inside loop.
x = 10 while x > 0: print(x) x -= 1 # Add this line
**Exercise 3 (Fix the List Mutation Bug):** This code should remove all even numbers from the list. Fix it.
numbers = [1, 2, 3, 4, 5, 6, 7, 8] for num in numbers: if num % 2 == 0: numbers.remove(num) print(numbers)
SolutionIterate over `numbers[:]`.
numbers = [1, 2, 3, 4, 5, 6, 7, 8] for num in numbers[:]: # Add [:] to iterate over a copy if num % 2 == 0: numbers.remove(num) print(numbers) # Output: [1, 3, 5, 7]

Exercise 4 (Fix the Indentation Bug): This code should print "Even" for even numbers and "Odd" for odd numbers. Fix the indentation.

for i in range(1, 6): if i % 2 == 0: print(f"{i} is even") print(f"{i} is odd")
SolutionIndent `else:` block correctly.
for i in range(1, 6): if i % 2 == 0: print(f"{i} is even") else: # Indent this! print(f"{i} is odd")

Exercise 5 (Fix break vs continue Bug): This code should skip all odd numbers and print only even numbers. Fix it.

for i in range(1, 11): if i % 2 == 1: break print(i)
SolutionChange `break` to `continue`.
for i in range(1, 11): if i % 2 == 1: continue # Change break to continue print(i)

📚 Homework Questions

Question 1: The Debugging Diary Below is a buggy program that has at least 5 different errors (syntax, logic, or runtime). Find and fix all of them. Explain in comments what each bug was and how you fixed it.

# Buggy program - should print the sum of all even numbers from 1 to 20 sum_even = 0 i = 0 while i <= 20 if i % 2 == 0 sum_even = sum_even + i i += 1 else: continue print("Sum of even numbers:", sum_even)
Sample Answer
# Fixed version sum_even = 0 i = 0 while i <= 20: if i % 2 == 0: sum_even += i i += 1 # moved outside if print("Sum of even numbers:", sum_even)

Bug fixes: missing colon, missing indentation, continue causing infinite loop, moved increment.

Question 2: The Infinite Loop Detector Write a function (or program) that takes a list of numbers and sums them. However, the list might contain a None value (which represents an infinite loop marker). If you encounter None, print "Error: Infinite loop detected" and stop processing. Test your solution with:

Sample Answer
def safe_sum(lst): total = 0 for item in lst: if item is None: print("Error: Infinite loop detected") return None total += item return total

Question 3: The Safe List Muter Write a program that asks the user for 10 numbers (use a loop) and stores them in a list. Then, using a safe approach (iterating over a copy), remove all numbers that are divisible by 3. Print both the original list and the cleaned list.

Sample Answer
nums = [] for _ in range(10): nums.append(int(input())) clean = [x for x in nums if x % 3 != 0] print("Original:", nums) print("Cleaned:", clean)

Question 4: The Debugging Challenge (Find All Errors) The following code is supposed to find the first occurrence of the number 42 in a list and print its index. If 42 is not found, it should print "Not found". The code has multiple bugs (off-by-one, indentation, logic). Find and fix all bugs.

numbers = [10, 20, 30, 42, 50, 60] found = False for i in range(0, len(numbers)) if numbers[i] == 42 found = True index = i break if found: print(f"Found 42 at index {i}") else: print("Not found")
Sample Answer
numbers = [10,20,30,42,50,60] found = False for i in range(len(numbers)): if numbers[i] == 42: found = True index = i break if found: print(f"Found 42 at index {index}") else: print("Not found")

Question 5: The Bug Report Write a program that deliberately contains three different types of loop bugs (e.g., off-by-one, infinite loop, and list mutation). Then, write a detailed "Bug Report" explaining:

  1. What each bug is.
  2. How to reproduce it.
  3. How to fix it.
  4. How to prevent it in the future.

Bonus: Exchange your buggy program with a classmate and see if they can find all the bugs!

Sample AnswerA good report includes: description of bug, reproduction steps, fix, prevention strategy. Example: off‑by‑one in `range(1,10)` → should be `range(1,11)`, prevention: test with small numbers.

Question 6: The Loop Validator Write a program that asks the user for a password. The password must:

Use a while loop to keep asking until the user enters a valid password. For each failed validation, print a specific error message indicating what went wrong. When the user enters a valid password, print "Password accepted!" and break out of the loop.

Hint: Use any(char.isupper() for char in password) or a loop with a flag to check each condition.

Sample Answer
while True: pw = input("Password: ") errors = [] if len(pw) < 8: errors.append("At least 8 characters") if not any(c.isupper() for c in pw): errors.append("At least one uppercase") if not any(c.isdigit() for c in pw): errors.append("At least one digit") if "password" in pw.lower(): errors.append("Cannot contain 'password'") if errors: for err in errors: print(err) else: print("Password accepted!") break

10. Summary: Common Errors Quick Reference

Error Type Symptom Fix
Off-by-One Loop runs one too many/few times Check range() upper bound; use <= vs < correctly
Infinite Loop Program freezes Ensure loop variable updates toward the condition
List Mutation Items are skipped or loop never ends Iterate over a copy (list[:]) or build a new list
Indentation Code runs at wrong time or IndentationError Use consistent 4 spaces; check which block code belongs to
Break vs Continue Loop exits early or skips incorrectly break = exit loop; continue = skip current iteration
Empty Loop Body never runs Check initial condition; add debug prints
Variable Leakage Unexpected values after loop Use explicit variables with sentinel values

11. Reflection and Next Steps

Congratulations! You've completed the debugging tutorial. Take a moment to reflect:

Key Takeaways:

  1. Read error messages carefully — they tell you exactly what's wrong and where.
  2. Use print statements to trace your program's execution.
  3. Test with small inputs before scaling up.
  4. Always update loop variables in while loops.
  5. Never modify lists during iteration — use a copy or build a new list.
  6. Watch your indentation — it matters in Python.
  7. Know the difference between break and continue — they serve completely different purposes.

Remember: Every bug you fix makes you a better programmer. Debugging is not a sign of failure; it's a sign that you're learning and growing. Keep practicing, and soon these errors will become second nature to spot and fix!

You have now completed all seven tutorials on loops! You've learned:

You now have a solid foundation in one of programming's most essential concepts. The skills you've developed here will serve you in every future programming project. Well done! 🎉

Previous | Tutorial index | Next