apache/beam · error

param rangeEndEstimator cannot be nil. Implementing…

Error message

param rangeEndEstimator cannot be nil. Implementing offsetrange.RangeEndEstimator may be required

What it means

NewGrowableTracker creates a restriction tracker for restrictions whose end may grow over time (e.g. an unbounded source). Because a growable tracker cannot estimate its end without a RangeEndEstimator, passing a nil estimator is rejected outright with this error. This is a defensive argument check in the Beam Go SDK.

Solutions

  1. Pass a non-nil RangeEndEstimator: implement EstimateEnded func(rest Restriction) bool (and RangeEndEstimatorFromRestrictionFn if needed) matching your restriction semantics.
  2. If your restriction is actually bounded and static, use offsetrange.NewTracker(rest) instead, which requires no estimator.
  3. For bounded restrictions with a growable tracker, you can use offsetrange.RangeEndEstimatorFromRestrictionFn(func(Restriction) bool { return true }) to treat them as ended.
  4. Guard the call site: only construct NewGrowableTracker when the source can genuinely grow.

Example fix

// before
tracker, err := offsetrange.NewGrowableTracker(rest, nil)

// after
tracker, err := offsetrange.NewGrowableTracker(rest,
    offsetrange.RangeEndEstimatorFromRestrictionFn(func(r offsetrange.Restriction) bool {
        return r.End == math.MaxInt64 // reached the true end
    }))
Defensive patterns

Strategy: validation

Validate before calling

if estimator == nil {
    return errors.New("offsetrange.NewGrowableTracker requires a non-nil RangeEndEstimator")
}
tracker, err := offsetrange.NewGrowableTracker(rest, estimator)

Type guard

func estimatorOk(e offsetrange.RangeEndEstimator) bool { return e != nil }

Prevention

When it happens

Trigger: Calling offsetrange.NewGrowableTracker(rest, nil) — e.g. in a custom source's createRTracker when the developer omits the estimator — or in tests exercising bad-parameter paths.

Common situations: Implementing a custom unbounded/growing DoFn or source in Beam Go and forgetting to supply an offsetrange.RangeEndEstimator implementation; copying example code that uses the regular offsetrange.NewTracker and swapping to NewGrowableTracker without the extra argument being meaningful.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/9e2005e0a7c63c6a. Report an issue: GitHub.

Appendix: source

Thrown at sdks/go/pkg/beam/io/rtrackers/offsetrange/offsetrange.go:274

// of block positions if needed.
type GrowableTracker struct {
	Tracker
	rangeEndEstimator RangeEndEstimator
}

// NewGrowableTracker creates a GrowableTracker for handling a growable offset range.
// math.MaxInt64 is used as the end of the range to indicate infinity for an unbounded range.
//
// An OffsetRange is considered growable when the end offset could grow (or change)
// during execution time (e.g. Kafka topic partition offset, appended file, ...).
//
// The growable range is marked as done by claiming math.MaxInt64-1.
//
// For bounded restrictions, this tracker works the same as offsetrange.Tracker.
// Use that directly if you have no need of estimating the end of a bound.
func NewGrowableTracker(rest Restriction, rangeEndEstimator RangeEndEstimator) (*GrowableTracker, error) {
	if rangeEndEstimator == nil {
		return nil, fmt.Errorf("param rangeEndEstimator cannot be nil. Implementing offsetrange.RangeEndEstimator may be required")
	}
	return &GrowableTracker{*NewTracker(Restriction{Start: rest.Start, End: rest.End}), rangeEndEstimator}, nil
}

// Start returns the starting range of the restriction tracked by a tracker.
func (tracker *GrowableTracker) Start() int64 {
	return tracker.GetRestriction().(Restriction).Start
}

// End returns the end range of the restriction tracked by a tracker.
func (tracker *GrowableTracker) End() int64 {
	return tracker.GetRestriction().(Restriction).End
}

func max(x, y int64) int64 {
	if x > y {
		return x
	}

View on GitHub (pinned to 12126d8942)