apache/beam · error · ValueError

The first record [starting at ] must be at a split point

Error message

The first record [starting at %d] must be at a split point

What it means

The very first record claimed in ordered-record mode must be flagged as a split point, because it defines the initial boundary of the range being processed. If the first claim arrives with split_point=False (last_record_start still -1), the tracker cannot establish its start boundary and raises this ValueError.

Solutions

  1. Mark the first claimed record as a split point: try_claim(first_offset) followed by set_current_position(first_offset, split_point=True).
  2. Ensure the source marks the initial record position as a split point before reading subsequent records.
  3. Review OffsetRangeTracker docs/usage in existing Beam sources and mirror their first-record handling.

Example fix

// before
tracker.try_claim(first_offset)
tracker.set_current_position(first_offset, split_point=False)
// after
tracker.try_claim(first_offset)
tracker.set_current_position(first_offset, split_point=True)
Defensive patterns

Strategy: validation

Validate before calling

is_first = tracker._last_record_start == -1
tracker.try_claim(first_offset)
tracker.set_current_position(first_offset, split_point=is_first)

Type guard

def needs_initial_split_point(tracker) -> bool:
    return tracker._last_record_start == -1

Try / catch

try:
    tracker.try_claim(first_offset)
    tracker.set_current_position(first_offset, split_point=False)
except ValueError as e:
    if 'must be at a split point' in str(e):
        tracker.set_current_position(first_offset, split_point=True)

Prevention

When it happens

Trigger: First call to try_claim(offset) / set_current_position(offset, split_point=False) on a fresh tracker (offset_of_last_split_point == -1 and last_record_start == -1).

Common situations: Custom source implementations that forget split_point=True on the first record after opening a file/stream; code paths that initialize the tracker mid-stream without marking the initial boundary.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

    # This function must only be called under the lock self.lock.
    if not self._lock.locked():
      raise ValueError(
          'This function must only be called under the lock self.lock.')

    if record_start < self._last_record_start:
      raise ValueError(
          'Trying to return a record [starting at %d] which is before the '
          'last-returned record [starting at %d]' %
          (record_start, self._last_record_start))

    if (split_point and self._offset_of_last_split_point != -1 and
        record_start == self._offset_of_last_split_point):
      raise ValueError(
          'Record at a split point has same offset as the previous split '
          'point: %d' % record_start)

    if not split_point and self._last_record_start == -1:
      raise ValueError(
          'The first record [starting at %d] must be at a split point' %
          record_start)

  def try_claim(self, record_start):
    with self._lock:
      # Attempted claim should be monotonous.
      if record_start <= self._last_attempted_record_start:
        raise ValueError(
            'Trying to return a record [starting at %d] which is not greater'
            'than the last-attempted record [starting at %d]' %
            (record_start, self._last_attempted_record_start))
      self._validate_record_start(record_start, True)
      self._last_attempted_record_start = record_start
      if record_start >= self.stop_position():
        return False
      self._offset_of_last_split_point = record_start
      self._last_record_start = record_start
      self._split_points_seen += 1

View on GitHub (pinned to 12126d8942)