temporalio/temporal · error
Unable to merge iterator range %v with incoming iterator ran
Error message
Unable to merge iterator range %v with incoming iterator range %v
What it means
IteratorImpl.Merge panics when CanMerge(iter) is false, meaning the incoming iterator's range is not contiguous/adjacent to this iterator's remaining range. Ranges can only merge when their union is itself a valid continuous range.
Source
Thrown at service/history/queues/iterator.go:98
leftRange, rightRange := i.remainingRange.Split(key)
left = NewIterator(
i.paginationFnProvider,
leftRange,
)
right = NewIterator(
i.paginationFnProvider,
rightRange,
)
return left, right
}
func (i *IteratorImpl) CanMerge(iter Iterator) bool {
return i.remainingRange.CanMerge(iter.Range())
}
func (i *IteratorImpl) Merge(iter Iterator) Iterator {
if !i.CanMerge(iter) {
panic(fmt.Sprintf("Unable to merge iterator range %v with incoming iterator range %v", i.remainingRange, iter.Range()))
}
return NewIterator(
i.paginationFnProvider,
i.remainingRange.Merge(iter.Range()),
)
}
func (i *IteratorImpl) Remaining() Iterator {
return NewIterator(
i.paginationFnProvider,
i.remainingRange,
)
}
View on GitHub (pinned to bde624efd1)
Solutions
- Check it.CanMerge(other) before calling Merge and skip or reorder otherwise
- Sort candidate iterators by range before merging so only adjacent ranges are paired
- Verify both iterators target the same queue/category and range universe
- Don't merge an iterator that has already been consumed; use its original Range() for planning
Example fix
// before
merged := it.Merge(other)
// after
if it.CanMerge(other) {
merged = it.Merge(other)
} else {
merged = it // keep separate
} Defensive patterns
Strategy: validation
Validate before calling
if !it.CanMerge(other) {
// keep iterators separate or reorder candidates
return
}
merged := it.Merge(other) Try / catch
func safeMerge(it queues.Iterator, other queues.Iterator) (merged queues.Iterator) {
defer func() {
if r := recover(); r != nil {
merged = it
}
}()
if !it.CanMerge(other) {
return it
}
return it.Merge(other)
} Prevention
- Sort ranges by InclusiveMin before merging
- Only merge iterators from the same queue and task category
- Merge before consuming iterators, not after partial iteration
- Add rebalancing tests that pair every iterator with its neighbor
When it happens
Trigger: Calling Merge with an iterator whose range has a gap or overlaps non-adjacently with the receiver's range — often after queue rebalancing paired iterators that were not neighbors.
Common situations: Rebalancing logic that groups iterators by key order incorrectly; merging iterators after one was partially consumed so its Range() shrank; mismatched sources (different queue/task types) producing non-adjacent ranges.
Related errors
- Iterator encountered Next call when there is no next item
- Unable to split iterator with range %v at %v
- Unable to merge range %v with incoming range %v
- Unable to merge scope with range %v with range %v by range
- HistoryEventIterator Next() called without checking HasNext(
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/ed49e230fca7e8df.
Report an issue: GitHub.