temporalio/temporal · error
Iterator encountered Next call when there is no next item
Error message
Iterator encountered Next call when there is no next item
What it means
IteratorImpl.Next panics if called when HasNext() is false, because the underlying paging iterator has no more tasks. It is a contract-enforcement panic: callers must check HasNext before Next, mirroring database/sql Rows and the Go range-over-iterator misuse pattern.
Source
Thrown at service/history/queues/iterator.go:55
paginationFnProvider: paginationFnProvider,
remainingRange: r,
// lazy initialized to prevent task pre-fetching on creating the iterator
pagingIterator: nil,
}
}
func (i *IteratorImpl) HasNext() bool {
if i.pagingIterator == nil {
i.pagingIterator = collection.NewPagingIterator(i.paginationFnProvider(i.remainingRange))
}
return i.pagingIterator.HasNext()
}
func (i *IteratorImpl) Next() (tasks.Task, error) {
if !i.HasNext() {
panic("Iterator encountered Next call when there is no next item")
}
task, err := i.pagingIterator.Next()
if err != nil {
return nil, err
}
i.remainingRange.InclusiveMin = task.GetKey().Next()
return task, nil
}
func (i *IteratorImpl) Range() Range {
return i.remainingRange
}
func (i *IteratorImpl) CanSplit(key tasks.Key) bool {
return i.remainingRange.CanSplit(key)
}View on GitHub (pinned to bde624efd1)
Solutions
- Guard every Next() with if !iter.HasNext() { break } in the loop
- Use a standard for iter.HasNext() { ... } loop structure
- Check whether an earlier error path consumed the final task and returned early
- Refactor to use the iterator in a range-helper function that encapsulates the HasNext check
Example fix
// before
for {
task, err := iter.Next()
...
}
// after
for iter.HasNext() {
task, err := iter.Next()
if err != nil {
return err
}
...
} Defensive patterns
Strategy: validation
Validate before calling
if !iter.HasNext() {
return nil // iterator exhausted
}
task, err := iter.Next() Try / catch
func safeNext(iter queues.Iterator) (t tasks.Task, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("iterator next on exhausted iterator: %v", r)
}
}()
if !iter.HasNext() {
return nil, nil
}
return iter.Next()
} Prevention
- Always loop with for iter.HasNext()
- Never call Next() after an error return without re-checking HasNext
- Handle empty ranges explicitly before iteration
- Prefer range-encapsulating helpers over manual Next calls
When it happens
Trigger: Calling Next() after the iterator is exhausted, or calling Next() on a freshly created iterator whose range is empty (inclusiveMin == exclusiveMax).
Common situations: Loop bugs like for { iter.Next() } without HasNext; continuing a loop after a task error return consumed the last item; empty queue ranges fetched during quiet periods.
Related errors
- Unable to split iterator with range %v at %v
- Unable to merge iterator range %v with incoming iterator ran
- HistoryEventIterator Next() called without checking HasNext(
- HistoryEventIterator Next() should return either a history e
- Found key with non-zero pending task count but has no corres
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/ef816e1f75468498.
Report an issue: GitHub.