apache/beam · error · ValueError

Start offset must not be 'None'

Error message

Start offset must not be 'None'

What it means

OffsetRangeTracker tracks progress over an integer offset range and cannot represent a None start. Its constructor validates immediately that start is an int (not None), raising ValueError otherwise. A None start would break position comparison and resume logic.

Source

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

_LOGGER = logging.getLogger(__name__)


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

View on GitHub (pinned to 12126d8942)

Solutions

  1. Pass an explicit integer start, e.g. OffsetRangeTracker(0, end).
  2. Fix the upstream code that computes the start offset so it never yields None.
  3. Validate/guard inputs before constructing the tracker: assert start_offset is not None.

Example fix

// before
OffsetRangeTracker(start_offset, end_offset)  # start_offset is None
// after
OffsetRangeTracker(start_offset if start_offset is not None else 0, end_offset)
Defensive patterns

Strategy: type-guard

Validate before calling

assert start_offset is not None, "start offset required"
assert isinstance(start_offset, int)

Type guard

def is_valid_offset(x) -> bool:
    return 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(0, end)

Prevention

When it happens

Trigger: OffsetRangeTracker(None, end) — constructing the tracker with a None start offset, typically because a source read produced no valid range.

Common situations: Custom BatchedSource/RestrictionSource implementations computing offsets from parsed input where the start came back None (missing file header, failed int conversion swallowed upstream).

Related errors


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