Debugging — Python Error Guide

Off-by-One —
The most common bug that never raises an exception.

Your loop runs one iteration too many, or one too few — and unlike most errors on this hub, it usually doesn't crash at all. The program just quietly produces a slightly wrong answer. Watch a loop's boundary on LearnBug and see exactly which element got skipped or double-counted.

No exceptionUsually silent
Loop boundaryRoot cause
PythonLanguage

What is an Off-by-One Error?

A loop's start or end boundary is wrong by exactly one position — starting one element too late, ending one element too early (or too late), or double-processing a boundary element. Sometimes this raises an IndexError; often it just silently produces a slightly wrong result with no error at all, which makes it harder to catch.

Why visualization helps

A wrong answer with no traceback gives you nothing to click on — you have to notice the output is subtly wrong first. LearnBug shows exactly which indices a loop actually visits, side by side with the list, so a skipped first element or a missed last element is visibly obvious instead of something you have to deduce from a wrong total.

LearnBug — the loop's actual visited indices next to the list
🖼 Add a LearnBug screenshot here
What Causes It

Three shapes of the same boundary mistake

Starting one index too late

range(1, len(x)) instead of range(len(x)) skips the first element entirely — no crash, just a silently wrong result.

Skipped first element

Ending one index too early

range(len(x) - 1) when you meant range(len(x)) skips the last element — this one also usually raises no exception at all.

Skipped last element

Ending one index too late

range(len(x) + 1) tries one index too many — this is the variant that DOES raise IndexError, since it goes out of bounds.

IndexError
Walkthrough

Summing [10, 20, 30, 40, 50] starting from index 1

1

range(1, len(numbers)) starts the loop at index 1, not 0

The intent was to sum every element, but the loop's starting index skips position 0 entirely. Index 0 holds the value 10 — it's silently excluded from the calculation.

10idx 0, skipped!
20idx 1, start
30
40
50
Loop range: 1 to 4 — index 0 is never visited
2

total accumulates 20 + 30 + 40 + 50 — no error, just a smaller sum than expected

Every one of these additions is a completely valid operation. Nothing here raises an exception — the loop runs to completion successfully, just over the wrong range.

20
30
40
50last added
total = 20 + 30 + 40 + 50 = 140 — should have been 150
3

print(total) shows 140 — off by exactly 10, the value at the skipped index

This is what makes off-by-one bugs dangerous: the program runs cleanly to completion and produces a number that looks plausible. You'd only notice by checking the math by hand.

Result: 140 instead of 150 — wrong by exactly the value at index 0, no exception raised

Code Example

The broken code — no traceback, just a wrong answer

PythonBroken — starts at index 1, skipping numbers[0]
numbers = [10, 20, 30, 40, 50]
total = 0

for i in range(1, len(numbers)):   # starts at 1 — skips numbers[0]
    total += numbers[i]

print(total)   # 140 — missing 10 from the sum
The Fix

Start at 0, or better — avoid manual indexing entirely

PythonFixed — two approaches
numbers = [10, 20, 30, 40, 50]

# Fix 1: correct the range
total = 0
for i in range(len(numbers)):
    total += numbers[i]
print(total)   # 150

# Fix 2: skip manual indexing entirely — sum() handles this correctly
print(sum(numbers))   # 150

Quick Reference

SymptomWrong result, often no crashOne element skipped or double-counted, with no exception in most variants
Typical causeWrong loop boundaryrange(1, ...) instead of range(0, ...), or an off +1/-1 somewhere
Fix patternCheck both ends of the rangeExplicitly verify the first and last elements the loop actually touches
Best preventionAvoid manual index mathPrefer sum(), enumerate(), or direct iteration over hand-written range() bounds when possible

Frequently asked questions

Why is this bug so common specifically?

Because "the count of elements" and "the last valid index" differ by exactly one, and it's easy to reach for the wrong one when writing a range by hand — especially under time pressure or when translating a 1-indexed mental model (like "the 5th item") into Python's 0-indexed reality. It's consistently one of the most common bugs across every programming language, not just Python.

Why doesn't this always raise an exception like IndexError?

It only raises when the loop tries to access an index that's genuinely out of bounds (too high). Starting too late or ending too early stays entirely within valid indices — it just doesn't visit all of them — so every operation succeeds, and the program finishes "normally" with a quietly wrong result.

How do I catch off-by-one bugs before they ship?

Test with small, hand-checkable inputs where you can verify the exact expected output — an empty list, a single-element list, and a short list you can sum by hand are usually enough to expose a boundary bug. Boundary conditions (the very first and very last elements specifically) deserve explicit test cases, not just "does it run without crashing."

Is there a general rule for getting loop boundaries right?

When possible, avoid writing the boundary by hand at all — use built-in functions (sum(), max()) or direct iteration (for item in list) that can't have this bug by construction. When you must use range(), remember range(len(x)) visits every valid index exactly once — any deviation from that needs a clear, deliberate reason.

Watch which indices your loop actually visits

Paste your code into LearnBug and see the skipped or double-counted element directly.

Open Playground →