krahets/hello-algo · error · IndexError
Double-ended queue is empty
Error message
Double-ended queue is empty
What it means
ArrayDeque.peek_first raises IndexError('Double-ended queue is empty') when the deque holds zero elements, blocking the read of self._nums[self._front] which would return stale/garbage data from the circular buffer. peek_first is also the backbone of pop_first, so the same failure surfaces through a pop_first call on an empty deque. The guard enforces that accessing the front is only valid when at least one element exists.
Source
Thrown at en/codes/python/chapter_stack_and_queue/array_deque.py:76
def pop_first(self) -> int:
"""Front of the queue dequeue"""
num = self.peek_first()
# Front pointer moves one position backward
self._front = self.index(self._front + 1)
self._size -= 1
return num
def pop_last(self) -> int:
"""Rear of the queue dequeue"""
num = self.peek_last()
self._size -= 1
return num
def peek_first(self) -> int:
"""Access front of the queue element"""
if self.is_empty():
raise IndexError("Double-ended queue is empty")
return self._nums[self._front]
def peek_last(self) -> int:
"""Access rear of the queue element"""
if self.is_empty():
raise IndexError("Double-ended queue is empty")
# Calculate tail element index
last = self.index(self._front + self._size - 1)
return self._nums[last]
def to_array(self) -> list[int]:
"""Return array for printing"""
# Only convert list elements within the valid length range
res = []
for i in range(self._size):
res.append(self._nums[self.index(self._front + i)])
return res
View on GitHub (pinned to 69932aed18)
Solutions
- Check the predicate first: `if not deque.is_empty(): head = deque.peek_first()`.
- Bound drain loops: `while not deque.is_empty(): x = deque.pop_first()`.
- Use deque.size() to drive counted loops and stop at zero.
- Where flow control by exception is intended, catch IndexError at the dequeue site only.
Example fix
// before front = deque.peek_first() # raises if empty // after front = deque.peek_first() if not deque.is_empty() else None
Defensive patterns
Strategy: validation
Validate before calling
if not deque.is_empty():
front = deque.peek_first() Type guard
def deque_nonempty(d) -> bool:
return not d.is_empty() Try / catch
try:
front = deque.peek_first()
except IndexError:
front = None Prevention
- Gate every peek/pop with is_empty().
- Bound counted loops with size().
- Wrap peek_first in a helper returning Optional for consumers that tolerate empty.
When it happens
Trigger: Calling peek_first() or pop_first() on a freshly constructed ArrayDeque with no pushes; calling after all elements were popped; interleaving pushes/pops where a pop on the last element is followed by another pop; loops that pop_front until empty without checking is_empty().
Common situations: Sliding-window and BFS-style algorithms that drain a deque; producer/consumer pacing where the consumer outruns the producer; off-by-one in counted dequeue loops; reusing a deque capacity object across rounds without tracking size.
Related errors
AI-assisted analysis of krahets/hello-algo@69932aed18 (2026-08-13).
Data as JSON: /api/errors/75a617a474f03579.
Report an issue: GitHub.