temporalio/temporal · error
Unable to merge range %v with incoming range %v
Error message
Unable to merge range %v with incoming range %v
What it means
Range.Merge panics when CanMerge(input) is false, i.e. the two ranges are neither adjacent nor overlapping, so their union would not be a continuous range. Merge is meant for combining neighboring task ranges during queue rebalancing.
Source
Thrown at service/history/queues/range.go:75
if !r.CanSplit(key) {
panic(fmt.Sprintf("Unable to split range %v at %v", r, key))
}
return NewRange(r.InclusiveMin, key), NewRange(key, r.ExclusiveMax)
}
func (r *Range) CanMerge(
input Range,
) bool {
return r.InclusiveMin.CompareTo(input.ExclusiveMax) <= 0 &&
r.ExclusiveMax.CompareTo(input.InclusiveMin) >= 0
}
func (r *Range) Merge(
input Range,
) Range {
if !r.CanMerge(input) {
panic(fmt.Sprintf("Unable to merge range %v with incoming range %v", r, input))
}
return NewRange(
tasks.MinKey(r.InclusiveMin, input.InclusiveMin),
tasks.MaxKey(r.ExclusiveMax, input.ExclusiveMax),
)
}
func (r *Range) Equals(
input Range,
) bool {
return r.InclusiveMin.CompareTo(input.InclusiveMin) == 0 &&
r.ExclusiveMax.CompareTo(input.ExclusiveMax) == 0
}
View on GitHub (pinned to bde624efd1)
Solutions
- Check r.CanMerge(input) before calling Merge
- Sort ranges by InclusiveMin and only merge consecutive neighbors
- Verify both ranges come from the same queue/category before merging
- If a gap must be covered, construct a new NewRange(min(mins), max(maxes)) explicitly instead of Merge
Example fix
// before
merged := r.Merge(input)
// after
if r.CanMerge(input) {
merged = r.Merge(input)
} else {
merged = tasks.NewRange(
tasks.MinKey(r.InclusiveMin, input.InclusiveMin),
tasks.MaxKey(r.ExclusiveMax, input.ExclusiveMax))
} Defensive patterns
Strategy: validation
Validate before calling
if !r.CanMerge(input) {
// ranges not adjacent: build a covering range explicitly if needed
return
}
merged := r.Merge(input) Try / catch
func safeMergeRange(r queues.Range, input queues.Range) (merged queues.Range) {
defer func() {
if rec := recover(); rec != nil {
merged = r
}
}()
if !r.CanMerge(input) {
return queues.Range{}
}
return r.Merge(input)
} Prevention
- Sort ranges and merge only adjacent neighbors
- Gate Merge behind CanMerge at every call site
- Confirm both ranges belong to the same queue/category
- Prefer explicit NewRange(min, max) when intentionally covering a gap
When it happens
Trigger: Calling Merge with a range whose InclusiveMin > r.ExclusiveMax or whose ExclusiveMax < r.InclusiveMin, typically when rebalancing logic pairs non-neighboring queue ranges.
Common situations: Rebalancing algorithms that iterate unordered maps of ranges so pairs aren't adjacent; ranges belonging to different queues or task categories; stale ranges after another shard already split/merged them.
Related errors
- Unable to merge iterator range %v with incoming iterator ran
- invalid task range, min %v is larger than max %v
- Unable to split range %v at %v
- Unable to merge scope with range %v with range %v by range
- 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/96d444e5fa567635.
Report an issue: GitHub.