{"record":{"id":"cc10bf71ac16794d","repo":"temporalio/temporal","slug":"unable-to-split-range-v-at-v","errorCode":null,"errorMessage":"Unable to split range %v at %v","messagePattern":"Unable to split range (.+?) at (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"service/history/queues/range.go","lineNumber":58,"sourceCode":"\nfunc (r *Range) ContainsRange(\n\tinput Range,\n) bool {\n\treturn r.InclusiveMin.CompareTo(input.InclusiveMin) <= 0 &&\n\t\tr.ExclusiveMax.CompareTo(input.ExclusiveMax) >= 0\n}\n\nfunc (r *Range) CanSplit(\n\tkey tasks.Key,\n) bool {\n\treturn r.ContainsKey(key) || r.ExclusiveMax.CompareTo(key) == 0\n}\n\nfunc (r *Range) Split(\n\tkey tasks.Key,\n) (left Range, right Range) {\n\tif !r.CanSplit(key) {\n\t\tpanic(fmt.Sprintf(\"Unable to split range %v at %v\", r, key))\n\t}\n\n\treturn NewRange(r.InclusiveMin, key), NewRange(key, r.ExclusiveMax)\n}\n\nfunc (r *Range) CanMerge(\n\tinput Range,\n) bool {\n\treturn r.InclusiveMin.CompareTo(input.ExclusiveMax) <= 0 &&\n\t\tr.ExclusiveMax.CompareTo(input.InclusiveMin) >= 0\n}\n\nfunc (r *Range) Merge(\n\tinput Range,\n) Range {\n\tif !r.CanMerge(input) {\n\t\tpanic(fmt.Sprintf(\"Unable to merge range %v with incoming range %v\", r, input))\n\t}","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/service/history/queues/range.go#L40-L76","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\nleft, right := r.Split(key)\n// after\nif !r.CanSplit(key) {\n    return r, Range{} // or pick a new interior key\n}\nleft, right = r.Split(key)","handlingStrategy":"validation","validationCode":"if !r.CanSplit(key) {\n    return r, queues.Range{} // degenerate: cannot split\n}\nleft, right := r.Split(key)","typeGuard":null,"tryCatchPattern":"func safeSplitRange(r queues.Range, key tasks.Key) (l, right queues.Range) {\n    defer func() {\n        if rec := recover(); rec != nil {\n            l, right = r, queues.Range{}\n        }\n    }()\n    if !r.CanSplit(key) {\n        return r, queues.Range{}\n    }\n    return r.Split(key)\n}","preventionTips":["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"],"tags":["go","panic","range-split","validation","history-service"],"backgroundTag":"invalid-split-key","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}