You called df.iloc[100] but the DataFrame has only 50 rows, so pandas threw IndexError: single positional indexer is out-of-bounds. Note: .iloc raises IndexError (position-based), while .loc raises KeyError (label-based). Knowing which is which saves debugging time.

📌 Quick answer: Guard with if len(df) > i: row = df.iloc[i]. For “last N rows” use df.tail(N). For “first N rows” use df.head(N). Both are safe on any size DataFrame, including empty ones.
Cause 1: Hardcoded position larger than DataFrame
You wrote df.iloc[100] in production code; user uploaded a 50-row CSV; crash.
df = pd.read_csv("upload.csv")
row = df.iloc[100] # ❌ IndexError if df has 50 rows
# Safe
if len(df) > 100:
row = df.iloc[100]
else:
row = NoneCause 2: Empty DataFrame after filter
Filter returned zero rows, df.iloc[0] now fails.
high = df[df["score"] > 100]
first = high.iloc[0] # ❌ if no row has score > 100
# Safe
if not high.empty:
first = high.iloc[0]Cause 3: Negative index too negative
df.iloc[-100] on a 50-row DataFrame fails.
df.iloc[-1] # ✓ last row
df.iloc[-50] # ✓ first row (wraps)
df.iloc[-51] # ❌ IndexErrorCause 4: iloc slice off the end (does NOT error)
df.iloc[10:20] on a 5-row DataFrame returns empty, NOT an error. Surprising if you expected an error.
small = pd.DataFrame({"a": [1, 2, 3]})
small.iloc[10:20] # ✓ returns empty DataFrame, no error
small.iloc[10] # ❌ raises IndexError
# Slice semantics differ from scalar semantics in pandasCause 5: iloc with negative slice and ambiguity
df.iloc[-3:0] returns empty (because -3 > 0 in slice terms, even though -3 means “row count minus 3” in normal indexing).
df.iloc[-3:] # ✓ last 3 rows
df.iloc[-3:0] # ⚠ empty (slice stop=0 means before first row)
df.iloc[:-3] # ✓ everything except last 3Prevention
- Use .head(N) and .tail(N) for safe top/bottom access
- Always check
not df.emptyorlen(df) > ibefore scalar iloc - Prefer slices over scalars:
df.iloc[0:1]returns empty on empty df instead of erroring - Don’t mix .iloc with .loc semantics:
.ilocis position,.locis label
Related Guides
- List index out of range (full guide)
- String index out of range
- All IndexError fixes
- Python Tutorial hub
Frequently Asked Questions
What’s the difference between iloc IndexError and loc KeyError?
iloc is position-based and raises IndexError when the position is out of range. loc is label-based and raises KeyError when the label doesn’t exist. Same DataFrame, different exception types depending on which accessor.
Why does df.iloc[100] error but df.iloc[10:20] doesn’t?
Scalar vs slice semantics. Scalar iloc raises IndexError when out of bounds. Slice iloc silently clips to the available range and returns whatever’s there (often empty). This is consistent with Python list slicing.
How do I safely access the first or last row of a DataFrame?
df.head(1) for first row (returns empty DataFrame if empty). df.tail(1) for last row. df.iloc[0] only if you’ve already verified df is non-empty.
Can negative iloc indexes go too negative?
Yes. df.iloc[-N] works for N up to len(df). df.iloc[-len(df)-1] raises IndexError. df.iloc[-N:] (slice form) clips safely and returns the last N rows or as many as exist.
Should I use iloc or loc?
iloc when you want ‘the Nth row by position regardless of label’. loc when you want ‘the row with this specific index label’. iloc is safer in pipelines because position doesn’t depend on the index being intact.
