temporalio/temporal · error

Cannot peek item because priority queue is empty

Error message

Cannot peek item because priority queue is empty

What it means

Peek() on priorityQueueImpl returns the top item but the queue is a strict data structure: calling Peek on an empty queue is a programming error, so the library panics instead of returning a zero value. This protects callers from silently processing a zero-value item that could corrupt ordering logic.

Source

Thrown at common/collection/priority_queue.go:43

// PriorityQueue will take ownership of the passed in items,
// so caller should stop modifying it.
// The complexity is O(n) where n is the number of items
func NewPriorityQueueWithItems[T any](
	compareLess func(this T, other T) bool,
	items []T,
) Queue[T] {
	pq := &priorityQueueImpl[T]{
		compareLess: compareLess,
		items:       items,
	}
	heap.Init(pq)
	return pq
}

// Peek returns the top item of the priority queue
func (pq *priorityQueueImpl[T]) Peek() T {
	if pq.IsEmpty() {
		panic("Cannot peek item because priority queue is empty")
	}
	return pq.items[0]
}

// Add push an item to priority queue
func (pq *priorityQueueImpl[T]) Add(item T) {
	heap.Push(pq, item)
}

// Remove pop an item from priority queue
func (pq *priorityQueueImpl[T]) Remove() T {
	return heap.Pop(pq).(T)
}

// IsEmpty indicate if the priority queue is empty
func (pq *priorityQueueImpl[T]) IsEmpty() bool {
	return pq.Len() == 0
}

View on GitHub (pinned to bde624efd1)

Solutions

  1. Check pq.IsEmpty() (or pq.Len() == 0) before calling Peek
  2. Restructure the drain loop to use the remove/pop operation's returned item instead of a Peek-then-Pop pair
  3. Guard concurrent producers/consumers so Peek is only reached when a size check and fill have both completed

Example fix

// before
item := pq.Peek()
// after
if pq.IsEmpty() {
    return // or wait for items
}
item := pq.Peek()
Defensive patterns

Strategy: validation

Validate before calling

if pq.Len() > 0 { _ = pq.Peek() }

Prevention

When it happens

Trigger: Calling pq.Peek() when no items have been added via Add() or after all items were removed (e.g. after popping the last element).

Common situations: Startup ordering bugs where a consumer drains the queue faster than producers fill it; off-by-one drain loops calling Peek after the final Pop; retry logic that re-peeks after removal.

Related errors


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/c7d45f47572d6581. Report an issue: GitHub.