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

  1. Check it.CanMerge(other) before calling Merge and skip or reorder otherwise
  2. Sort candidate iterators by range before merging so only adjacent ranges are paired
  3. Verify both iterators target the same queue/category and range universe
  4. 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

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


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