krahets/hello-algo · error · Error

Heap is empty

Error message

Heap is empty

What it means

Thrown by pop() on a max-heap when the heap is empty (isEmpty() returns true). pop() swaps root with the last element, removes the last, then sifts down the root. The guard ensures no undefined is returned and no swap against a non-existent index. This is a state guard, not a bounds check on arguments.

Source

Thrown at en/codes/javascript/chapter_heap/my_heap.js:85

    /* Starting from node i, heapify from bottom to top */
    #siftUp(i) {
        while (true) {
            // Get parent node of node i
            const p = this.#parent(i);
            // When "crossing root node" or "node needs no repair", end heapify
            if (p < 0 || this.#maxHeap[i] <= this.#maxHeap[p]) break;
            // Swap two nodes
            this.#swap(i, p);
            // Loop upward heapify
            i = p;
        }
    }

    /* Element exits heap */
    pop() {
        // Handle empty case
        if (this.isEmpty()) throw new Error('Heap is empty');
        // Delete node
        this.#swap(0, this.size() - 1);
        // Remove node
        const val = this.#maxHeap.pop();
        // Return top element
        this.#siftDown(0);
        // Return heap top element
        return val;
    }

    /* Starting from node i, heapify from top to bottom */
    #siftDown(i) {
        while (true) {
            // If node i is largest or indices l, r are out of bounds, no need to continue heapify, break
            const l = this.#left(i),
                r = this.#right(i);
            let ma = i;
            if (l < this.size() && this.#maxHeap[l] > this.#maxHeap[ma]) ma = l;

View on GitHub (pinned to 69932aed18)

Solutions

  1. Check heap.isEmpty() / heap.size() === 0 before calling pop.
  2. In a drain loop, use while (!heap.isEmpty()) { const v = heap.pop(); ... }.
  3. Track the count of pushes separately if you need to pop a fixed number.
  4. Wrap pop() in try/catch only as a last resort; prefer the explicit emptiness check.

Example fix

// before
while (true) { const v = heap.pop(); ... } // throws when empty

// after
while (!heap.isEmpty()) { const v = heap.pop(); ... }
Defensive patterns

Strategy: validation

Validate before calling

if (!heap.isEmpty()) {
  const v = heap.pop();
} else {
  // handle empty heap
}

Type guard

function heapHasElements(heap) {
  return typeof heap.isEmpty === 'function' && !heap.isEmpty();
}

Try / catch

try {
  const v = heap.pop();
} catch (e) {
  if (e.message === 'Heap is empty') { /* drained */ }
  else throw e;
}

Prevention

When it happens

Trigger: Calling heap.pop() on a freshly constructed or fully drained heap; calling pop() more times than the number of pushed elements; interleaving peek()/pop() without tracking size.

Common situations: Drain loop that pops until 'falsy' (undefined never reached because throw happens first); event-loop priority queue that processes faster than it receives; forgetting that pop requires prior push.

Related errors


AI-assisted analysis of krahets/hello-algo@69932aed18 (2026-08-13). Data as JSON: /api/errors/f59a9c892de245c5. Report an issue: GitHub.