krahets/hello-algo · error · IndexError

Heap is empty

Error message

Heap is empty

What it means

Raised by `MyMaxHeap#pop` (my_heap.rb:83, English version) when `is_empty?` is true. Identical contract to the Chinese counterpart: the method swaps root with last leaf, removes the last element, then sifts down. On an empty heap there is nothing to swap or remove, so the guard rejects the call before touching `@max_heap`.

Source

Thrown at en/codes/ruby/chapter_heap/my_heap.rb:83

  ### Heapify from node i, bottom to top ###
  def sift_up(i)
    loop do
      # Get parent node of node i
      p = parent(i)
      # When "crossing root node" or "node needs no repair", end heapify
      break if p < 0 || @max_heap[i] <= @max_heap[p]
      # Swap two nodes
      swap(i, p)
      # Loop upward heapify
      i = p
    end
  end

  ### Pop element from heap ###
  def pop
    # Handle empty case
    raise IndexError, "Heap is empty" if is_empty?
    # Delete node
    swap(0, size - 1)
    # Remove node
    val = @max_heap.pop
    # Return top element
    sift_down(0)
    # Return heap top element
    val
  end

  ### Heapify from node i, top to bottom ###
  def sift_down(i)
    loop do
      # If node i is largest or indices l, r are out of bounds, no need to continue heapify, break
      l, r, ma = left(i), right(i), i
      ma = l if l < size && @max_heap[l] > @max_heap[ma]
      ma = r if r < size && @max_heap[r] > @max_heap[ma]

View on GitHub (pinned to 69932aed18)

Solutions

  1. Guard every pop with `unless heap.is_empty?`.
  2. Drive the drain loop with `until heap.is_empty?`.
  3. Rescue IndexError if empty pops are recoverable.

Example fix

# before
while i < n
  heap.pop
end

# after
until heap.is_empty?
  heap.pop
end
Defensive patterns

Strategy: validation

Validate before calling

return nil if heap.is_empty?
heap.pop

Type guard

def heap_popable?(heap)
  heap.respond_to?(:is_empty?) && heap.respond_to?(:pop) && !heap.is_empty?
end

Try / catch

begin
  heap.pop
rescue IndexError
  nil
end

Prevention

When it happens

Trigger: Calling `heap.pop` when the heap's internal `@max_heap` array is empty. Occurs after draining the heap, before any push, or in a heap-sort loop with an off-by-one count.

Common situations: Heap-sort driver that pops `n+1` times; priority queue drained to empty then popped; test that pops without checking size.

Related errors


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