krahets/hello-algo · error · IndexError
Double-ended queue is empty
Error message
Double-ended queue is empty
What it means
LinkedListDeque.pop (the internal dequeue used by pop_first/pop_last) raises IndexError('Double-ended queue is empty') when is_empty() is true, before attempting to dereference self._front.val / self._rear.val. Because the linked-list head/tail pointers are None when empty, the guard prevents an AttributeError on .val and gives a clear message. Both pop_first and pop_last route through this method.
Source
Thrown at en/codes/python/chapter_stack_and_queue/linkedlist_deque.py:66
else:
# Add node to the tail of the linked list
self._rear.next = node
node.prev = self._rear
self._rear = node # Update tail node
self._size += 1 # Update queue length
def push_first(self, num: int):
"""Front of the queue enqueue"""
self.push(num, True)
def push_last(self, num: int):
"""Rear of the queue enqueue"""
self.push(num, False)
def pop(self, is_front: bool) -> int:
"""Dequeue operation"""
if self.is_empty():
raise IndexError("Double-ended queue is empty")
# Front of the queue dequeue operation
if is_front:
val: int = self._front.val # Temporarily store head node value
# Delete head node
fnext: ListNode | None = self._front.next
if fnext is not None:
fnext.prev = None
self._front.next = None
self._front = fnext # Update head node
# Rear of the queue dequeue operation
else:
val: int = self._rear.val # Temporarily store tail node value
# Delete tail node
rprev: ListNode | None = self._rear.prev
if rprev is not None:
rprev.next = None
self._rear.prev = None
self._rear = rprev # Update tail nodeView on GitHub (pinned to 69932aed18)
Solutions
- Guard with the predicate: `if not deque.is_empty(): deque.pop_first()`.
- Bound drains: `while not deque.is_empty(): ...`.
- Use deque.size() to drive counted loops.
- Wrap pop in a helper returning Optional when empty states are expected.
Example fix
// before val = deque.pop_first() # raises if empty // after val = deque.pop_first() if not deque.is_empty() else None
Defensive patterns
Strategy: validation
Validate before calling
if not deque.is_empty():
val = deque.pop_first() Type guard
def deque_nonempty(d) -> bool:
return not d.is_empty() Try / catch
try:
val = deque.pop_first()
except IndexError:
val = None Prevention
- Drive linked-deque drains with `while not deque.is_empty()`.
- Bound counted loops with size().
- Wrap pop_first/pop_last in helpers returning Optional.
When it happens
Trigger: Calling pop_first()/pop_last() on an empty deque; calling pop after the last node was detached; unguarded drain loops alternating front/rear pops; deque used as a stack/queue drained past empty.
Common situations: BFS/DFS with a linked deque; sliding-window deque emptied by pops; undo/redo on a linked structure; algorithms that pop from whichever end is cheaper without checking size.
Related errors
AI-assisted analysis of krahets/hello-algo@69932aed18 (2026-08-13).
Data as JSON: /api/errors/601eb07b74ab085b.
Report an issue: GitHub.