{"record":{"id":"c7d45f47572d6581","repo":"temporalio/temporal","slug":"cannot-peek-item-because-priority-queue-is-empty","errorCode":null,"errorMessage":"Cannot peek item because priority queue is empty","messagePattern":"Cannot peek item because priority queue is empty","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/collection/priority_queue.go","lineNumber":43,"sourceCode":"// PriorityQueue will take ownership of the passed in items,\n// so caller should stop modifying it.\n// The complexity is O(n) where n is the number of items\nfunc NewPriorityQueueWithItems[T any](\n\tcompareLess func(this T, other T) bool,\n\titems []T,\n) Queue[T] {\n\tpq := &priorityQueueImpl[T]{\n\t\tcompareLess: compareLess,\n\t\titems:       items,\n\t}\n\theap.Init(pq)\n\treturn pq\n}\n\n// Peek returns the top item of the priority queue\nfunc (pq *priorityQueueImpl[T]) Peek() T {\n\tif pq.IsEmpty() {\n\t\tpanic(\"Cannot peek item because priority queue is empty\")\n\t}\n\treturn pq.items[0]\n}\n\n// Add push an item to priority queue\nfunc (pq *priorityQueueImpl[T]) Add(item T) {\n\theap.Push(pq, item)\n}\n\n// Remove pop an item from priority queue\nfunc (pq *priorityQueueImpl[T]) Remove() T {\n\treturn heap.Pop(pq).(T)\n}\n\n// IsEmpty indicate if the priority queue is empty\nfunc (pq *priorityQueueImpl[T]) IsEmpty() bool {\n\treturn pq.Len() == 0\n}","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/collection/priority_queue.go#L25-L61","documentation":"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.","triggerScenarios":"Calling pq.Peek() when no items have been added via Add() or after all items were removed (e.g. after popping the last element).","commonSituations":"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.","solutions":["Check pq.IsEmpty() (or pq.Len() == 0) before calling Peek","Restructure the drain loop to use the remove/pop operation's returned item instead of a Peek-then-Pop pair","Guard concurrent producers/consumers so Peek is only reached when a size check and fill have both completed"],"exampleFix":"// before\nitem := pq.Peek()\n// after\nif pq.IsEmpty() {\n    return // or wait for items\n}\nitem := pq.Peek()","handlingStrategy":"validation","validationCode":"if pq.Len() > 0 { _ = pq.Peek() }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check IsEmpty before Peek","Use Pop-with-ok APIs in drain loops"],"tags":["go","panic","data-structure","empty-collection"],"backgroundTag":"peek-on-empty-collection","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}