{"record":{"id":"9b8049735d0d5e5b","repo":"apache/beam","slug":"offsetrestrictiontracker-is-not-done-since-work-in-range-s-s","errorCode":null,"errorMessage":"OffsetRestrictionTracker is not done since work in range [%s, %s) has not been claimed.","messagePattern":"OffsetRestrictionTracker is not done since work in range \\[(.+?), (.+?)\\) has not been claimed\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/restriction_trackers.py","lineNumber":89,"sourceCode":"\n\nclass OffsetRestrictionTracker(RestrictionTracker):\n  \"\"\"An `iobase.RestrictionTracker` implementations for an offset range.\n\n  Offset range is represented as OffsetRange.\n  \"\"\"\n  def __init__(self, offset_range: OffsetRange) -> None:\n    assert isinstance(offset_range, OffsetRange), offset_range\n    self._range = offset_range\n    self._current_position = None\n    self._last_claim_attempt = None\n    self._checkpointed = False\n\n  def check_done(self):\n    if (self._range.start != self._range.stop and\n        (self._last_claim_attempt is None or\n         self._last_claim_attempt < self._range.stop - 1)):\n      raise ValueError(\n          'OffsetRestrictionTracker is not done since work in range [%s, %s) '\n          'has not been claimed.' % (\n              self._last_claim_attempt\n              if self._last_claim_attempt is not None else self._range.start,\n              self._range.stop))\n\n  def current_restriction(self):\n    return self._range\n\n  def current_progress(self) -> RestrictionProgress:\n    if self._current_position is None:\n      fraction = 0.0\n    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) /","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/restriction_trackers.py#L71-L107","documentation":"OffsetRestrictionTracker.check_done() verifies that a splittable-DoFn restriction has been fully claimed before the element can be considered processed. Beam throws this ValueError when the range is non-empty but the last claim attempt never reached stop-1, meaning unclaimed work remains in [start, stop).","triggerScenarios":"Finishing a DoFn process_element without claiming every position in the range — e.g. claiming only positions 0..4 for OffsetRange(0, 10), or never calling try_claim at all before check_done.","commonSituations":"Custom splittable DoFns with early loop exits (break on a condition), skipped offsets inside a while loop, or forgetting to claim the final offset.","solutions":["Ensure the processing loop claims every position up to stop-1, e.g. while tracker.try_claim(position)","Remove early breaks/exits that skip claims, or checkpoint instead of exiting","If the range is genuinely unprocessed, return a deferred residual rather than declaring done","Verify the restriction passed to the tracker matches the offsets actually iterated"],"exampleFix":"// before\nwhile position < restriction.stop:\n  if not tracker.try_claim(position):\n    break\n  if some_condition:\n    break  # leaves offsets unclaimed\n  position += 1\n// after\nwhile tracker.try_claim(position):\n  process(position)\n  if some_condition:\n    return a_deferred_residual(position)  # let Beam resume later\n  position += 1","handlingStrategy":"validation","validationCode":"def restriction_is_done(tracker):\n    rng = tracker.current_restriction()\n    return rng.start == rng.stop or tracker.last_claim_reached_stop()","typeGuard":null,"tryCatchPattern":"try:\n    tracker.check_done()\nexcept ValueError as e:\n    raise RuntimeError('SDF left unclaimed work; check process loop') from e","preventionTips":["Drive iteration with while tracker.try_claim(pos) instead of manual bounds","Never break out of a claim loop without returning a residual deferral","Test custom SDFs with small ranges to catch off-by-one claims"],"tags":["apache-beam","python","splittable-dofn"],"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-14T16:17:12.679Z"}