Previous | Tutorial index | Next
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.
Debugging is a skill, not a failure. Even experienced programmers spend most of their time debugging. The key is having a systematic approach:
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.
This is arguably the most common loop error. It happens when a loop runs one too many or one too few times.
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!"
<= 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
❌ 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])
range(), test with small numbers first. If you want to loop from 1 to 5, write range(1, 6) and test with print(list(range(1, 6))) to verify.enumerate() when you need both index and value—it eliminates indexing errors.while LoopsAn infinite loop is when the loop condition never becomes False. This freezes your program.
❌ 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)
❌ 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)
❌ 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
Keyboard Interrupt: Press Ctrl + C (or Cmd + C on Mac) to force-stop your program.
Print Statements: Add print(f"x = {x}") at the top of the loop to see what's happening.
Use a Debugger: Step through the code line by line to see the variables change.
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
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.
❌ 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?
index = 0, 1, 2, 3, 4i = 0 (num=1): Remove 1. List becomes [2, 3, 4, 5].i = 1 (num=3): The loop moves to index 1, which is now 3 (we skipped 2!).i = 2 (num=5): Remove 5. List becomes [2, 4].2 and 4 were never checked!✅ 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]
❌ 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.
Python uses indentation to define code blocks. Incorrect indentation is the most common syntax error for beginners.
❌ WRONG - IndentationError:
for i in range(3):
print(i) # IndentationError: expected an indented block
✅ RIGHT:
for i in range(3):
print(i) # Properly indented
❌ 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
❌ 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).
break and continueThese are often used incorrectly, especially in while loops.
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
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
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!
Sometimes your loop condition is never True to begin with, so the loop body never runs.
❌ 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.
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
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}")
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).
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}")
Explain your code line by line to an inanimate object (like a rubber duck). Often, saying it out loud makes the bug obvious.
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
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
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]
Q4: (True/False) continue exits the loop immediately.
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
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)
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
x = 10
while x > 0:
print(x)
x -= 1 # Add this line
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
for num in numbers:
if num % 2 == 0:
numbers.remove(num)
print(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")
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)
for i in range(1, 11):
if i % 2 == 1:
continue # Change break to continue
print(i)
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)
# 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:
[1, 2, 3, 4, 5] → Should sum to 15[1, 2, None, 3, 4] → Should print the error messagedef 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.
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")
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:
Bonus: Exchange your buggy program with a classmate and see if they can find all the bugs!
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.
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
| 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 |
Congratulations! You've completed the debugging tutorial. Take a moment to reflect:
Key Takeaways:
while loops.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:
for loops (Tutorial 1)while loops (Tutorial 2)break and continue (Tutorial 3)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! 🎉