{"record":{"id":"d5fda942882c0027","repo":"apache/beam","slug":"record-at-a-split-point-has-same-offset-as-the-previous-d5fda9","errorCode":null,"errorMessage":"Record at a split point has same offset as the previous split point: %d","messagePattern":"Record at a split point has same offset as the previous split point: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/range_trackers.py","lineNumber":108,"sourceCode":"    no matter `try_claim()` returns `True` or `False`.\n    \"\"\"\n    return self._last_attempted_record_start\n\n  def _validate_record_start(self, record_start, split_point):\n    # 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","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/range_trackers.py#L90-L126","documentation":"Dynamic work rebalancing (splitting) can only occur at split points. If two consecutive records claim to be at a split point with the same start offset, the tracker cannot distinguish them, so _validate_record_start raises this ValueError to preserve unique split-point offsets.","triggerScenarios":"Calling try_claim(offset) then set_current_position(offset, split_point=True) — or two consecutive split-point claims — with the same offset as the previously recorded split point.","commonSituations":"Custom source incorrectly marking every record (or the same record) as a split point; a source that re-claims a boundary record after resuming.","solutions":["Mark only genuinely distinct positions as split points; ensure each split point has a strictly new offset.","Only pass split_point=True for the position currently being split; use split_point=False for continuation records.","Fix source logic that advances offsets so split points move forward."],"exampleFix":"// before\ntracker.try_claim(100)\ntracker.set_current_position(100, split_point=True)  # duplicate split point\n// after\ntracker.try_claim(100)\ntracker.set_current_position(150, split_point=True)  # new offset","handlingStrategy":"validation","validationCode":"if split_point and tracker._offset_of_last_split_point == record_start:\n    split_point = False  # same offset as previous split point","typeGuard":"def is_new_split_point(record_start: int, last_split: int) -> bool:\n    return last_split == -1 or record_start != last_split","tryCatchPattern":"try:\n    tracker.set_current_position(offset, split_point=True)\nexcept ValueError as e:\n    if 'same offset as the previous split' in str(e):\n        tracker.set_current_position(offset, split_point=False)","preventionTips":["Mark exactly one split point per distinct offset.","Only flag split points where dynamic splitting is actually valid.","Test custom sources under work-rebalancing (split) conditions."],"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"}