{"record":{"id":"75a617a474f03579","repo":"krahets/hello-algo","slug":"double-ended-queue-is-empty","errorCode":null,"errorMessage":"Double-ended queue is empty","messagePattern":"Double-ended queue is empty","errorType":"exception","errorClass":"IndexError","httpStatus":null,"severity":"error","filePath":"en/codes/python/chapter_stack_and_queue/array_deque.py","lineNumber":76,"sourceCode":"\n    def pop_first(self) -> int:\n        \"\"\"Front of the queue dequeue\"\"\"\n        num = self.peek_first()\n        # Front pointer moves one position backward\n        self._front = self.index(self._front + 1)\n        self._size -= 1\n        return num\n\n    def pop_last(self) -> int:\n        \"\"\"Rear of the queue dequeue\"\"\"\n        num = self.peek_last()\n        self._size -= 1\n        return num\n\n    def peek_first(self) -> int:\n        \"\"\"Access front of the queue element\"\"\"\n        if self.is_empty():\n            raise IndexError(\"Double-ended queue is empty\")\n        return self._nums[self._front]\n\n    def peek_last(self) -> int:\n        \"\"\"Access rear of the queue element\"\"\"\n        if self.is_empty():\n            raise IndexError(\"Double-ended queue is empty\")\n        # Calculate tail element index\n        last = self.index(self._front + self._size - 1)\n        return self._nums[last]\n\n    def to_array(self) -> list[int]:\n        \"\"\"Return array for printing\"\"\"\n        # Only convert list elements within the valid length range\n        res = []\n        for i in range(self._size):\n            res.append(self._nums[self.index(self._front + i)])\n        return res\n","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/krahets/hello-algo/blob/69932aed1891a7b7f6a0de88cd116d3fe13e7032/en/codes/python/chapter_stack_and_queue/array_deque.py#L58-L94","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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."],"exampleFix":"// before\nfront = deque.peek_first()  # raises if empty\n// after\nfront = deque.peek_first() if not deque.is_empty() else None","handlingStrategy":"validation","validationCode":"if not deque.is_empty():\n    front = deque.peek_first()","typeGuard":"def deque_nonempty(d) -> bool:\n    return not d.is_empty()","tryCatchPattern":"try:\n    front = deque.peek_first()\nexcept IndexError:\n    front = None","preventionTips":["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."],"tags":["deque","indexerror","empty-state","python","data-structures"],"backgroundTag":null,"analyzedSha":"69932aed1891a7b7f6a0de88cd116d3fe13e7032","analyzedAt":"2026-08-13T23:02:37.581Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}