krahets/hello-algo · error · RangeError
Heap is empty.
Error message
Heap is empty.
What it means
A RangeError 'Heap is empty.' thrown by pop() in MaxHeap (my_heap.ts:84). pop() swaps the root with the last leaf, removes the tail, then sifts the new root down; on an empty heap there is nothing to swap or return, so the guard refuses the operation. peek() is NOT guarded, so it would return undefined on empty — pop is the only method that throws here.
Source
Thrown at zh-hant/codes/typescript/chapter_heap/my_heap.ts:84
/* 從節點 i 開始,從底至頂堆積化 */
private siftUp(i: number): void {
while (true) {
// 獲取節點 i 的父節點
const p = this.parent(i);
// 當“越過根節點”或“節點無須修復”時,結束堆積化
if (p < 0 || this.maxHeap[i] <= this.maxHeap[p]) break;
// 交換兩節點
this.swap(i, p);
// 迴圈向上堆積化
i = p;
}
}
/* 元素出堆積 */
public pop(): number {
// 判空處理
if (this.isEmpty()) throw new RangeError('Heap is empty.');
// 交換根節點與最右葉節點(交換首元素與尾元素)
this.swap(0, this.size() - 1);
// 刪除節點
const val = this.maxHeap.pop();
// 從頂至底堆積化
this.siftDown(0);
// 返回堆積頂元素
return val;
}
/* 從節點 i 開始,從頂至底堆積化 */
private siftDown(i: number): void {
while (true) {
// 判斷節點 i, l, r 中值最大的節點,記為 ma
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
- Guard every pop with `if (!heap.isEmpty()) heap.pop()` or loop with `while (!heap.isEmpty())`.
- Call isEmpty() (or size() === 0) before pop rather than relying on peek().
- Track the count you pushed and pop at most that many times.
- If a sentinel/undefined result is acceptable, wrap pop in a helper that returns undefined on empty.
Example fix
// before: off-by-one pops the empty heap and throws
while (heap.size() >= 0) {
heap.pop();
}
// after: stop when empty
while (!heap.isEmpty()) {
const top = heap.pop();
} Defensive patterns
Strategy: validation
Validate before calling
// Never pop an empty heap
function safePop(heap: MaxHeap): number | undefined {
return heap.isEmpty() ? undefined : heap.pop();
}
// usage:
while (!heap.isEmpty()) {
const top = heap.pop();
} Type guard
// Heap exposes isEmpty(); treat it as the emptiness guard const hasElements = (heap: MaxHeap): boolean => !heap.isEmpty();
Try / catch
try {
const top = heap.pop();
} catch (e) {
if (e instanceof RangeError && /Heap is empty/.test(e.message)) {
// nothing to pop; handle empty state
} else throw e;
} Prevention
- Always loop with `while (!heap.isEmpty())`, not a fixed or `>= 0` bound.
- peek() is NOT guarded and returns undefined on empty — do not use it to test for data.
- Track the count of pushes and pop at most that many times.
- When draining, prefer isEmpty() over comparing size() to a cached number.
When it happens
Trigger: Calling maxHeap.pop() when maxHeap.size() === 0 (i.e., maxHeap.isEmpty() is true). This happens when draining a heap in a loop and popping one more time than the number of elements, or calling pop on a freshly constructed `new MaxHeap()` with no initial array.
Common situations: Off-by-one in a while-pop loop (using `while (true)` or `<= size` instead of `while (!heap.isEmpty())`); popping after a previous pop already emptied the heap; treating peek()'s undefined as 'still has data' and then popping; draining a heap built from an empty or undefined input array.
Related errors
AI-assisted analysis of krahets/hello-algo@69932aed18 (2026-08-13).
Data as JSON: /api/errors/d9ec2d1318bd70d9.
Report an issue: GitHub.