temporalio/temporal · error
Unable to split range %v at %v
Error message
Unable to split range %v at %v
What it means
Range.Split panics when CanSplit(key) is false — the split key is not strictly inside the range (it must be > InclusiveMin and < ExclusiveMax). Splitting at or outside the boundaries would produce an empty or invalid sub-range.
Source
Thrown at service/history/queues/range.go:58
func (r *Range) ContainsRange(
input Range,
) bool {
return r.InclusiveMin.CompareTo(input.InclusiveMin) <= 0 &&
r.ExclusiveMax.CompareTo(input.ExclusiveMax) >= 0
}
func (r *Range) CanSplit(
key tasks.Key,
) bool {
return r.ContainsKey(key) || r.ExclusiveMax.CompareTo(key) == 0
}
func (r *Range) Split(
key tasks.Key,
) (left Range, right Range) {
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))
}View on GitHub (pinned to bde624efd1)
Solutions
- Check r.CanSplit(key) before calling Split
- Clamp the split key to strictly interior: max(key, InclusiveMin+1) and ensure < ExclusiveMax
- Handle degenerate ranges (no interior point) by returning the range unsplit
- In random range generation, draw split keys from the open interval (InclusiveMin, ExclusiveMax)
Example fix
// before
left, right := r.Split(key)
// after
if !r.CanSplit(key) {
return r, Range{} // or pick a new interior key
}
left, right = r.Split(key) Defensive patterns
Strategy: validation
Validate before calling
if !r.CanSplit(key) {
return r, queues.Range{} // degenerate: cannot split
}
left, right := r.Split(key) Try / catch
func safeSplitRange(r queues.Range, key tasks.Key) (l, right queues.Range) {
defer func() {
if rec := recover(); rec != nil {
l, right = r, queues.Range{}
}
}()
if !r.CanSplit(key) {
return r, queues.Range{}
}
return r.Split(key)
} Prevention
- Gate Split behind CanSplit everywhere
- Generate random split keys strictly inside (InclusiveMin, ExclusiveMax)
- Treat single-task ranges as unsplittable
- Test boundary keys in range helpers
When it happens
Trigger: Calling Split with key <= r.InclusiveMin or key >= r.ExclusiveMax, e.g. NewRandomOrderedRangesInRange computing split points at range edges, or callers reusing a stale key after the range advanced.
Common situations: Random split-point generation hitting boundary values; single-task ranges (min == max-epsilon) where no interior key exists; off-by-one in shard-splitting logic.
Related errors
- Unable to split iterator with range %v at %v
- invalid task range, min %v is larger than max %v
- Unable to merge range %v with incoming range %v
- Unable to split scope with range %v at %v
- Unable to split queue slice with range %v at %v
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/cc10bf71ac16794d.
Report an issue: GitHub.