Skip to main content

How to Resolve "IndexError: Pop From an Empty Deque in Python" in Python

The IndexError: pop from an empty deque is a runtime error that occurs when you call pop() or popleft() on a deque that contains no elements. A deque (double-ended queue) from Python's collections module is a highly efficient data structure for adding and removing elements from both ends. However, attempting to remove an element from an empty deque will always raise this error.

In this guide, you will learn what causes this error, see common scenarios where it appears, and discover multiple reliable ways to prevent or handle it.

What Causes This Error?

The pop() method removes and returns the rightmost element of a deque, while popleft() removes and returns the leftmost element. Both methods require at least one element to be present. When the deque is empty, there is nothing to remove, so Python raises an IndexError:

from collections import deque

empty_deque = deque()
result = empty_deque.pop()

Output:

Traceback (most recent call last):
File "main.py", line 4, in <module>
result = empty_deque.pop()
IndexError: pop from an empty deque

The same error occurs with popleft():

from collections import deque

empty_deque = deque()
result = empty_deque.popleft() # Also raises IndexError

Common Scenarios That Trigger the Error

Scenario 1: Popping Without Checking if the Deque Is Empty

The most basic case: calling pop() on a deque without first verifying it has elements.

from collections import deque

my_deque = deque()
# ❌ No check before popping
result = my_deque.pop()

Output:

IndexError: pop from an empty deque

Scenario 2: Over-Popping Inside a Loop

When a loop attempts to pop more elements than the deque contains, it will crash once the deque runs out:

from collections import deque

my_deque = deque([1, 2])

# ❌ Loop runs 5 times, but deque only has 2 elements
for _ in range(5):
result = my_deque.pop()
print(result)

Output:

2
1
Traceback (most recent call last):
File "main.py", line 6, in <module>
result = my_deque.pop()
IndexError: pop from an empty deque

Scenario 3: Concurrent or Conditional Logic Drains the Deque

In more complex programs, the deque might be emptied by one part of the code while another part still expects elements to be available:

from collections import deque

tasks = deque(["task1", "task2"])

# First consumer drains the deque
while tasks:
tasks.pop()

# ❌ Second consumer assumes tasks are still available
next_task = tasks.pop()

Output:

IndexError: pop from an empty deque

Solution 1: Check if the Deque Is Not Empty Before Popping

The simplest and most Pythonic approach is to check the deque's truthiness before calling pop(). An empty deque evaluates to False, while a non-empty one evaluates to True:

from collections import deque

my_deque = deque()

if my_deque:
result = my_deque.pop()
print(f"Popped: {result}")
else:
print("The deque is empty: nothing to pop.")

Output:

The deque is empty: nothing to pop.

This works because Python's deque implements __bool__(), which returns False when the deque has no elements.

Solution 2: Use a while Loop Instead of a Fixed-Range for Loop

When you want to pop all elements from a deque, use a while loop that checks the deque's state on each iteration:

from collections import deque

my_deque = deque([10, 20, 30, 40])

# ✅ Safe: loop stops when the deque is empty
while my_deque:
result = my_deque.pop()
print(f"Popped: {result}")

print("All elements processed.")

Output:

Popped: 40
Popped: 30
Popped: 20
Popped: 10
All elements processed.

Compare this with the incorrect fixed-range approach:

from collections import deque

my_deque = deque([10, 20, 30])

# ❌ Dangerous: assumes the deque has at least 5 elements
for _ in range(5):
result = my_deque.pop()

Output:

IndexError: pop from an empty deque
tip

Always prefer while my_deque: over for _ in range(len(my_deque)): when draining a deque. The while approach naturally stops when the deque is empty, while the range approach captures the length once and does not adapt if the deque is modified by other code during iteration.

Solution 3: Use a try/except Block

When the empty state is an exceptional condition (not the normal flow), wrapping the pop() call in a try/except block is appropriate:

from collections import deque

my_deque = deque()

try:
result = my_deque.pop()
print(f"Popped: {result}")
except IndexError:
print("Cannot pop: the deque is empty.")

Output:

Cannot pop: the deque is empty.

This is especially useful in producer-consumer patterns where the deque is expected to have elements but might occasionally be empty:

from collections import deque

task_queue = deque()

def process_next_task(queue):
try:
task = queue.popleft()
print(f"Processing: {task}")
except IndexError:
print("No tasks available: waiting...")

process_next_task(task_queue)

Output:

No tasks available: waiting...

Solution 4: Use len() for Explicit Size Checks

If you prefer explicit numeric checks (for example, when you need to ensure a minimum number of elements), use len():

from collections import deque

my_deque = deque([42])

if len(my_deque) >= 1:
result = my_deque.pop()
print(f"Popped: {result}")
else:
print("Not enough elements to pop.")

Output:

Popped: 42

This is useful when your logic requires popping multiple elements at once:

from collections import deque

my_deque = deque([1, 2, 3])

# Need to pop 2 elements: check first
if len(my_deque) >= 2:
a = my_deque.pop()
b = my_deque.pop()
print(f"Popped {a} and {b}")
else:
print("Not enough elements.")

Output:

Popped 3 and 2

Solution 5: Create a Safe Wrapper Function

If you pop from deques frequently throughout your codebase, create a reusable helper function with a default value:

from collections import deque

def safe_pop(d, default=None):
"""Pop from the right end of the deque, returning default if empty."""
try:
return d.pop()
except IndexError:
return default

def safe_popleft(d, default=None):
"""Pop from the left end of the deque, returning default if empty."""
try:
return d.popleft()
except IndexError:
return default


# Usage
my_deque = deque()

result = safe_pop(my_deque, default="Nothing here")
print(result)

my_deque.append(99)
result = safe_pop(my_deque, default="Nothing here")
print(result)

Output:

Nothing here
99
Why not use dict.pop(key, default) syntax?

Unlike dict.pop(), the deque.pop() method does not accept a default value parameter. This is a deliberate design choice since deques are sequence types, not mappings. The wrapper function above fills this gap.

Quick Comparison of Solutions

ApproachBest ForProsCons
if my_deque:Simple single-pop operationsClean, Pythonic, no overheadOnly checks once: not safe in concurrent scenarios
while my_deque:Draining all elementsNaturally stops when emptyNot suitable for fixed-count pops
try/exceptExceptional empty statesHandles edge cases gracefullySlight overhead from exception handling
len() checkPopping multiple elements at onceExplicit and clear intentMore verbose
Wrapper functionRepeated use across a codebaseReusable, supports default valuesExtra function call overhead

Conclusion

The IndexError: pop from an empty deque is always caused by attempting to remove an element from a deque that has no elements. The fix is straightforward: always verify the deque has elements before popping.

  • For simple cases, check the deque's truthiness with if my_deque:.
  • For draining operations, use while my_deque:.
  • For exceptional conditions, wrap the call in try/except IndexError.
  • For codebases that pop frequently, create a safe wrapper function with a default return value.

Whichever approach you choose, the key principle is the same: never assume a deque has elements without checking first.