{"record":{"id":"408415c50fa805af","repo":"apache/beam","slug":"the-first-record-starting-at-d-must-be-at-a-split-point-408415","errorCode":null,"errorMessage":"The first record [starting at %d] must be at a split point","messagePattern":"The first record \\[starting at (.+?)\\] must be at a split point","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/range_trackers.py","lineNumber":113,"sourceCode":"    # This function must only be called under the lock self.lock.\n    if not self._lock.locked():\n      raise ValueError(\n          'This function must only be called under the lock self.lock.')\n\n    if record_start < self._last_record_start:\n      raise ValueError(\n          'Trying to return a record [starting at %d] which is before the '\n          'last-returned record [starting at %d]' %\n          (record_start, self._last_record_start))\n\n    if (split_point and self._offset_of_last_split_point != -1 and\n        record_start == self._offset_of_last_split_point):\n      raise ValueError(\n          'Record at a split point has same offset as the previous split '\n          'point: %d' % record_start)\n\n    if not split_point and self._last_record_start == -1:\n      raise ValueError(\n          'The first record [starting at %d] must be at a split point' %\n          record_start)\n\n  def try_claim(self, record_start):\n    with self._lock:\n      # Attempted claim should be monotonous.\n      if record_start <= self._last_attempted_record_start:\n        raise ValueError(\n            'Trying to return a record [starting at %d] which is not greater'\n            'than the last-attempted record [starting at %d]' %\n            (record_start, self._last_attempted_record_start))\n      self._validate_record_start(record_start, True)\n      self._last_attempted_record_start = record_start\n      if record_start >= self.stop_position():\n        return False\n      self._offset_of_last_split_point = record_start\n      self._last_record_start = record_start\n      self._split_points_seen += 1","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/range_trackers.py#L95-L131","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Mark the first claimed record as a split point: try_claim(first_offset) followed by set_current_position(first_offset, split_point=True).","Ensure the source marks the initial record position as a split point before reading subsequent records.","Review OffsetRangeTracker docs/usage in existing Beam sources and mirror their first-record handling."],"exampleFix":"// before\ntracker.try_claim(first_offset)\ntracker.set_current_position(first_offset, split_point=False)\n// after\ntracker.try_claim(first_offset)\ntracker.set_current_position(first_offset, split_point=True)","handlingStrategy":"validation","validationCode":"is_first = tracker._last_record_start == -1\ntracker.try_claim(first_offset)\ntracker.set_current_position(first_offset, split_point=is_first)","typeGuard":"def needs_initial_split_point(tracker) -> bool:\n    return tracker._last_record_start == -1","tryCatchPattern":"try:\n    tracker.try_claim(first_offset)\n    tracker.set_current_position(first_offset, split_point=False)\nexcept ValueError as e:\n    if 'must be at a split point' in str(e):\n        tracker.set_current_position(first_offset, split_point=True)","preventionTips":["Always mark the first record of a range as a split point.","Follow the pattern used by built-in Beam sources when wrapping OffsetRangeTracker.","Unit-test the first-claim path of custom sources."],"tags":["python","apache-beam","range-tracker","split-point","offset"],"backgroundTag":"invalid-state-transition","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}