{"record":{"id":"6758d0bb8f5cf705","repo":"apache/beam","slug":"trying-to-return-a-record-starting-at-d-which-is-before-the","errorCode":null,"errorMessage":"Trying to return a record [starting at %d] which is before the last-returned record [starting at %d]","messagePattern":"Trying to return a record \\[starting at (.+?)\\] which is before the last-returned record \\[starting at (.+?)\\]","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/range_trackers.py","lineNumber":101,"sourceCode":"\n  @property\n  def last_attempted_record_start(self):\n    \"\"\"Return current value of last_attempted_record_start.\n\n    last_attempted_record_start records a valid position that tried to be\n    claimed by calling try_claim(). This value is only updated by `try_claim()`\n    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.","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/range_trackers.py#L83-L119","documentation":"In ordered-record mode, OffsetRangeTracker requires record start positions to be monotonically non-decreasing. When _validate_record_start sees a record starting before the last returned record's start, it raises this ValueError because resuming from such a position would duplicate or skip records.","triggerScenarios":"try_claim(record_start) with a record_start lower than the previously returned record's start; calling set_current_position with a regressed position.","commonSituations":"Custom sources that re-read a file from the beginning after failure without resuming from the last checkpoint; out-of-order record delivery when reading unsorted data; rewinding an iterator on retry.","solutions":["Ensure records are claimed in non-decreasing start-offset order (sort or resume from the last returned position).","Resume from the last attempted position (tracker._last_attempted_record_start) after a failure instead of restarting at 0.","Do not re-claim already-returned records; advance past them."],"exampleFix":"// before\ntracker.try_claim(current_record.offset)  # < last returned offset\n// after\nif current_record.offset >= tracker._last_attempted_record_start:\n    tracker.try_claim(current_record.offset)","handlingStrategy":"validation","validationCode":"if tracker._last_record_start != -1 and record_start < tracker._last_record_start:\n    raise ValueError(\"record start regressed\")","typeGuard":"def is_monotonic(record_start: int, last: int) -> bool:\n    return last == -1 or record_start >= last","tryCatchPattern":"try:\n    tracker.try_claim(record_start)\nexcept ValueError as e:\n    if 'before the last-returned record' in str(e):\n        resume_from(tracker._last_attempted_record_start)","preventionTips":["Resume from the checkpointed position after failures instead of restarting.","Sort or index source data so offsets are non-decreasing.","Never rewind the underlying iterator during retries."],"tags":["python","apache-beam","range-tracker","ordering","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"}