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
- Pass a non-nil RangeEndEstimator: implement EstimateEnded func(rest Restriction) bool (and RangeEndEstimatorFromRestrictionFn if needed) matching your restriction semantics.
- If your restriction is actually bounded and static, use offsetrange.NewTracker(rest) instead, which requires no estimator.
- For bounded restrictions with a growable tracker, you can use offsetrange.RangeEndEstimatorFromRestrictionFn(func(Restriction) bool { return true }) to treat them as ended.
- 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
- Use NewTracker for bounded restrictions; reserve NewGrowableTracker for truly growing restrictions.
- Wrap tracker construction in a helper that supplies a default estimator so callers can't pass nil.
- Add a unit test exercising tracker creation for every custom source.
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
- failed to split: splittable unit was nil
- Primary restriction %#v is not done. Check that the…
- cannot claim a position lower than the previously claimed…
- cannot claim work after restriction tracker returns false
- cannot make a keyed iterable for an unkeyed side input
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)