temporalio/temporal · error

invalid task range, min %v is larger than max %v

Error message

invalid task range, min %v is larger than max %v

What it means

NewRange validates that the inclusive min key does not exceed the exclusive max key, panicking otherwise. A task range must satisfy InclusiveMin <= ExclusiveMax; anything else is a programming error in range construction.

Source

Thrown at service/history/queues/range.go:21

import (
	"fmt"

	"go.temporal.io/server/service/history/tasks"
)

type (
	Range struct {
		InclusiveMin tasks.Key
		ExclusiveMax tasks.Key
	}
)

func NewRange(
	inclusiveMin tasks.Key,
	exclusiveMax tasks.Key,
) Range {
	if inclusiveMin.CompareTo(exclusiveMax) > 0 {
		panic(fmt.Sprintf("invalid task range, min %v is larger than max %v", inclusiveMin, exclusiveMax))
	}

	return Range{
		InclusiveMin: inclusiveMin,
		ExclusiveMax: exclusiveMax,
	}
}

func (r *Range) IsEmpty() bool {
	return r.InclusiveMin.CompareTo(r.ExclusiveMax) == 0
}

func (r *Range) ContainsKey(
	key tasks.Key,
) bool {
	return key.CompareTo(r.InclusiveMin) >= 0 &&
		key.CompareTo(r.ExclusiveMax) < 0
}

View on GitHub (pinned to bde624efd1)

Solutions

  1. Validate/swap arguments: if tasks.Key.Compare(min, max) > 0, swap before calling NewRange
  2. Fix the upstream computation that produced inverted bounds (e.g. Split or time-window logic)
  3. Use tasks.MinKey/tasks.MaxKey when combining bounds to guarantee ordering
  4. Add a test asserting the caller's range-building helper for edge-case keys

Example fix

// before
r := tasks.NewRange(loadedMax, loadedMin) // inverted
// after
if loadedMin.CompareTo(loadedMax) > 0 {
    loadedMin, loadedMax = loadedMax, loadedMin
}
r := tasks.NewRange(loadedMin, loadedMax)
Defensive patterns

Strategy: validation

Validate before calling

if inclusiveMin.CompareTo(exclusiveMax) > 0 {
    inclusiveMin, exclusiveMax = exclusiveMax, inclusiveMin
}
r := tasks.NewRange(inclusiveMin, exclusiveMax)

Try / catch

func safeNewRange(minKey, maxKey tasks.Key) (r queues.Range) {
    defer func() {
        if rec := recover(); rec != nil {
            r = queues.Range{}
        }
    }()
    return tasks.NewRange(minKey, maxKey)
}

Prevention

When it happens

Trigger: Calling NewRange with min > max — e.g. computing boundaries in the wrong order, a Split producing inverted halves, or a completed range whose bounds crossed after updates.

Common situations: Negative or zero task IDs skewing key comparison; time-based keys built with start/stop swapped; tests or utilities constructing ranges from user input without ordering.

Related errors


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