You used collections.deque as a fast queue, called q.popleft(), and at some point got IndexError: pop from an empty deque. deque has the same empty-on-pop trap as list, with a few extra behaviors (maxlen, rotate, slicing limits) to know about.

📌 Quick answer: Guard popleft and pop with if my_deque:. Use maxlen on creation to cap size (older items auto-drop, no error). For random access, deque does NOT support slicing (use list(deque)[i:j] if needed).
Cause 1: popleft / pop on empty deque
Same empty-on-pop trap as list.pop, with a slightly different error message.
from collections import deque
q = deque([1, 2, 3])
while q:
item = q.popleft() # ✓ loop exits when q empty
# ❌ if you forgot the loop guard:
q.popleft()
q.popleft()
q.popleft()
q.popleft() # IndexError: pop from an empty dequeCause 2: Indexing past length
deque supports positional indexing but with the same out-of-range rules as list.
q = deque([1, 2, 3])
q[0] # ✓ 1
q[2] # ✓ 3
q[5] # ❌ IndexErrorCause 3: Slicing not supported
deque does NOT support slice notation. Convert to list first.
q = deque([1, 2, 3, 4, 5])
q[1:3] # ❌ TypeError: sequence index must be integer, not slice
# Workaround
sublist = list(q)[1:3] # ✓ [2, 3]Cause 4: maxlen for auto-cap (silent drop)
deque with maxlen never raises on push; older items silently drop off the opposite end.
recent = deque(maxlen=3)
recent.append(1); recent.append(2); recent.append(3); recent.append(4)
list(recent) # ✓ [2, 3, 4] (1 was dropped, no error)Prevention
- Use
while q:as the consumer loop condition - Add maxlen on creation when you only need the last N items
- For random access patterns, consider list (O(1) indexing) or deque + .rotate() carefully
- Catch IndexError if you need to detect drain-completion without an explicit empty check
Related Guides
- List index out of range (full guide)
- String index out of range
- All IndexError fixes
- Python Tutorial hub
Quick step-by-step summary (click to expand)
- Check deque length before positional access. Use if len(mydeque) greater than 0 before calling popleft or accessing mydeque[0].
- Use try/except IndexError for consumer loops. For queue draining patterns, wrap popleft in try except and break on IndexError to exit cleanly.
- Set maxlen for auto-eviction. deque(maxlen=100) auto-evicts old items so you never grow beyond a fixed size.
- Convert to list for known-safe access. When you need positional access at various indices, convert to list first: items = list(mydeque).
Why IndexError happens
List index out of range means you accessed my_list[i] beyond the list’s actual length. Python lists are indexed from 0 to len(list)-1.
Common triggers
- Off-by-one.
my_list[len(my_list)]fails — uselen(my_list) - 1. - Empty container.
my_list[0]fails when the list is empty. - Wrong data source. CSV had fewer columns than expected.
- Loop range wrong.
for i in range(len(my_list) + 1)— off-by-one. - API returned empty result. Unhandled empty response.
Diagnostic pattern
# BAD — accessing first element without check
def get_first(items):
return items[0] # IndexError if items is empty
# GOOD — guard for empty
def get_first(items):
if not items:
return None
return items[0]
# BETTER — use Optional and let caller handle
from typing import Optional, Sequence, TypeVar
T = TypeVar("T")
def get_first(items: Sequence[T]) -> Optional[T]:
return items[0] if items else None
# For pandas, use .iloc with .empty check
import pandas as pd
def first_row(df: pd.DataFrame) -> Optional[dict]:
if df.empty:
return None
return df.iloc[0].to_dict()
# For enumerate-based loops, this is safe
for i, item in enumerate(items):
print(i, item) # never IndexError
# Never write: for i in range(len(items) + 1)
Best practices
- Prefer enumerate over range(len()). Never off-by-one.
- Guard empty containers. Return None or default before accessing.
- Use slicing.
items[:5]is safe even if items has fewer than 5 elements. - Use type hints with Optional. Communicates that the value may not exist.
- Use pytest with edge cases. Test empty lists, single-element lists, off-by-one boundaries.
Official documentation
Frequently Asked Questions
What does ‘pop from an empty deque’ mean?
You called deque.popleft() or deque.pop() on an empty deque. Same idea as list IndexError on empty list. Guard with ‘if my_deque:’ or catch IndexError.
Is collections.deque faster than list?
For FIFO queues: yes. deque.popleft() and appendleft() are O(1) vs list.pop(0) and insert(0, …) which are O(n). For LIFO stacks: both are O(1) at the right end.
Can I slice a deque?
Not directly with [start:end]. Convert to list first: list(my_deque)[1:3]. The conversion is O(n) so for large deques consider whether random-access slicing is the right pattern.
What does deque(maxlen=N) do?
A bounded deque. When you push to the full deque, the oldest element on the opposite end silently drops. Common for rolling windows: store the last N samples without manual trimming.
How is deque.rotate() useful?
Cyclic shift: deque.rotate(1) moves every element right by 1 (last wraps to front). deque.rotate(-1) shifts left. O(k) where k is the rotation count. Useful for circular buffers or implementing carousels.
