TheAlgorithms/Python · error · IndexError

remove_first from empty list

Error message

remove_first from empty list

What it means

LinkedDeque.remove_first() raises IndexError('remove_first from empty list') when _delete would be called on the header sentinel itself. This is the correct, documented behavior (see doctest) and the exception type matches CPython's collections.deque.popleft on empty.

Source

Thrown at data_structures/linked_list/deque_doubly.py:122

    def remove_first(self):
        """removal from the front
        >>> d = LinkedDeque()
        >>> d.is_empty()
        True
        >>> d.remove_first()
        Traceback (most recent call last):
           ...
        IndexError: remove_first from empty list
        >>> d.add_first('A') # doctest: +ELLIPSIS
        <data_structures.linked_list.deque_doubly.LinkedDeque object at ...
        >>> d.remove_first()
        'A'
        >>> d.is_empty()
        True
        """
        if self.is_empty():
            raise IndexError("remove_first from empty list")
        return self._delete(self._header._next)

    def remove_last(self):
        """removal in the end
        >>> d = LinkedDeque()
        >>> d.is_empty()
        True
        >>> d.remove_last()
        Traceback (most recent call last):
           ...
        IndexError: remove_first from empty list
        >>> d.add_first('A') # doctest: +ELLIPSIS
        <data_structures.linked_list.deque_doubly.LinkedDeque object at ...
        >>> d.remove_last()
        'A'
        >>> d.is_empty()
        True
        """

View on GitHub (pinned to f5988cc097)

Solutions

  1. Check is_empty() before each remove_first() in loops: 'while not d.is_empty(): item = d.remove_first()'.
  2. Catch IndexError specifically — this method (unlike first()/last()) raises IndexError, so 'except IndexError' is precise here.
  3. Track in-flight counts so a failed consumer does not issue a compensating extra pop.

Example fix

# before
item = d.remove_first()  # IndexError on empty
# after
item = d.remove_first() if not d.is_empty() else None
Defensive patterns

Strategy: validation

Validate before calling

while not d.is_empty():
    item = d.remove_first()

Try / catch

try:
    item = d.remove_first()
except IndexError:
    item = None  # deque drained

Prevention

When it happens

Trigger: Calling remove_first() more times than elements were added: e.g. add_first('A') once, then remove_first() twice; or calling it on a brand-new LinkedDeque.

Common situations: Unbalanced pop/push loops where consumers drain faster than producers; worker loops that assume a blocking queue but this deque is non-blocking; retry logic that pops an item then pops again on failure.

Related errors


AI-assisted analysis of TheAlgorithms/Python@f5988cc097 (2026-08-14). Data as JSON: /api/errors/8c609472057909d3. Report an issue: GitHub.