{"record":{"id":"cc396b673a5776a9","repo":"apache/beam","slug":"positions-claimed-should-strictly-increase-trying-to-claim","errorCode":null,"errorMessage":"Positions claimed should strictly increase. Trying to claim position %d while last claim attempt was %d.","messagePattern":"Positions claimed should strictly increase\\. Trying to claim position (.+?) while last claim attempt was (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/restriction_trackers.py","lineNumber":120,"sourceCode":"    elif self._range.stop == self._range.start:\n      # If self._current_position is not None, we must be done.\n      fraction = 1.0\n    else:\n      fraction = (\n          float(self._current_position - self._range.start) /\n          (self._range.stop - self._range.start))\n    return RestrictionProgress(fraction=fraction)\n\n  def start_position(self):\n    return self._range.start\n\n  def stop_position(self):\n    return self._range.stop\n\n  def try_claim(self, position):\n    if (self._last_claim_attempt is not None and\n        position <= self._last_claim_attempt):\n      raise ValueError(\n          'Positions claimed should strictly increase. Trying to claim '\n          'position %d while last claim attempt was %d.' %\n          (position, self._last_claim_attempt))\n\n    self._last_claim_attempt = position\n    if position < self._range.start:\n      raise ValueError(\n          'Position to be claimed cannot be smaller than the start position '\n          'of the range. Tried to claim position %r for the range [%r, %r)' %\n          (position, self._range.start, self._range.stop))\n\n    if self._range.start <= position < self._range.stop:\n      self._current_position = position\n      return True\n\n    return False\n\n  def try_split(self, fraction_of_remainder):","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/restriction_trackers.py#L102-L138","documentation":"OffsetRestrictionTracker.try_claim() requires claimed positions to strictly increase so Beam can track deterministic progress through a restriction. Beam raises this ValueError when a position is claimed that is less than or equal to the previous claim attempt, since it would loop forever or double-claim work.","triggerScenarios":"Calling try_claim with the same position twice (position == last claim) or going backwards (position < last claim), e.g. re-claiming after a failed claim attempt or a loop that decrements the cursor.","commonSituations":"Buggy custom SDF loops that retry an offset, iterators that restart from the beginning, or checkpoint/resume logic that resets the position variable but not the tracker.","solutions":["Advance the position monotonically before each try_claim call","Never reuse a position value across try_claim calls; keep a separate cursor","If resuming after checkpoint, resume from stop_position()/current_position rather than the original start","Restructure loops that decrement or reset the index"],"exampleFix":"// before\nfor attempt in range(3):\n  tracker.try_claim(position)  # same position retried\n// after\ntracker.try_claim(position)\nposition += 1  # always move forward before the next claim","handlingStrategy":"validation","validationCode":"def safe_claim(tracker, position, last):\n    if last is not None and position <= last:\n        raise ValueError('positions must strictly increase')\n    return tracker.try_claim(position)","typeGuard":null,"tryCatchPattern":"try:\n    tracker.try_claim(position)\nexcept ValueError as e:\n    logger.error('Non-monotonic claim: %s', e)\n    position = tracker.current_position() + 1","preventionTips":["Keep a single monotonic cursor variable for claims","Never reset the cursor after checkpoint — resume from tracker state","Avoid retry loops that re-claim the same position"],"tags":["apache-beam","python","splittable-dofn"],"backgroundTag":"invalid-argument-value","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}