apache/beam · error · ValueError

End offset must not be 'None'

Error message

End offset must not be 'None'

What it means

OffsetRangeTracker requires an explicit end offset; None is not a representable position (unbounded end is expressed via the OFFSET_INFINITY sentinel, float('inf')). The constructor raises ValueError when end is None so range tracking and split logic never encounter an undefined position.

Source

Thrown at sdks/python/apache_beam/io/range_trackers.py:56


class OffsetRangeTracker(iobase.RangeTracker):
  """A 'RangeTracker' for non-negative positions of type 'long'."""

  # Offset corresponding to infinity. This can only be used as the upper-bound
  # of a range, and indicates reading all of the records until the end without
  # specifying exactly what the end is.
  # Infinite ranges cannot be split because it is impossible to estimate
  # progress within them.
  OFFSET_INFINITY = float('inf')

  def __init__(self, start, end):
    super().__init__()

    if start is None:
      raise ValueError('Start offset must not be \'None\'')
    if end is None:
      raise ValueError('End offset must not be \'None\'')
    assert isinstance(start, int)
    if end != self.OFFSET_INFINITY:
      assert isinstance(end, int)

    assert start <= end

    self._start_offset = start
    self._stop_offset = end

    self._last_record_start = -1
    self._last_attempted_record_start = -1
    self._offset_of_last_split_point = -1
    self._lock = threading.Lock()

    self._split_points_seen = 0
    self._split_points_unclaimed_callback = None

  def start_position(self):

View on GitHub (pinned to 12126d8942)

Solutions

  1. Pass OFFSET_INFINITY for an unbounded end: OffsetRangeTracker(start, OffsetRangeTracker.OFFSET_INFINITY).
  2. Pass a concrete integer end offset.
  3. Fix upstream computation of end so it never returns None; validate before constructing.

Example fix

// before
OffsetRangeTracker(start, None)
// after
OffsetRangeTracker(start, OffsetRangeTracker.OFFSET_INFINITY)
Defensive patterns

Strategy: type-guard

Validate before calling

assert end_offset is not None, "end offset required"
if end_offset is None:
    end_offset = OffsetRangeTracker.OFFSET_INFINITY

Type guard

def is_valid_end(x) -> bool:
    return x == float('inf') or (isinstance(x, int) and not isinstance(x, bool))

Try / catch

try:
    tracker = OffsetRangeTracker(start, end)
except ValueError as e:
    logger.error("Bad offset range: %s", e)
    tracker = OffsetRangeTracker(start, OffsetRangeTracker.OFFSET_INFINITY)

Prevention

When it happens

Trigger: OffsetRangeTracker(start, None) — constructing the tracker without an end. Use OffsetRangeTracker.OFFSET_INFINITY for unbounded ranges.

Common situations: Custom bounded/unbounded source implementations passing None instead of the infinity sentinel; computing end from file size that failed to resolve.

Related errors


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