Skip to content

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

How to fix the IndexError: pop from an empty deque in Python by checking deque size, using try/except, or implementing safe wrapper functions for reliable element removal.

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()
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])

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"])

while tasks:
    tasks.pop()

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])

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])

for _ in range(5):
    result = my_deque.pop()

Output:

IndexError: pop from an empty deque

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])

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


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

Quick Comparison of Solutions

Approach Best For Pros Cons
if my_deque: Simple single-pop operations Clean, Pythonic, no overhead Only checks once: not safe in concurrent scenarios
while my_deque: Draining all elements Naturally stops when empty Not suitable for fixed-count pops
try/except Exceptional empty states Handles edge cases gracefully Slight overhead from exception handling
len() check Popping multiple elements at once Explicit and clear intent More verbose
Wrapper function Repeated use across a codebase Reusable, supports default values Extra 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.

Navigation