{"record":{"id":"c3c37c9c47ed54b4","repo":"apache/beam","slug":"this-function-must-only-be-called-under-the-lock-self-lock","errorCode":null,"errorMessage":"This function must only be called under the lock self.lock.","messagePattern":"This function must only be called under the lock self\\.lock\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/range_trackers.py","lineNumber":97,"sourceCode":"\n  @property\n  def last_record_start(self):\n    return self._last_record_start\n\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)","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/range_trackers.py#L79-L115","documentation":"_validate_record_start is an internal consistency check of OffsetRangeTracker's ordered-record mode and must be invoked while holding self._lock (as try_claim and set_current_position do). If it is entered without the lock held, the tracker's state could be read/updated concurrently, so it raises ValueError to flag misuse of the internal API.","triggerScenarios":"Calling _validate_record_start (or a custom subclass path) directly from outside a `with self._lock:` block; overriding try_claim/set_current_position in a subclass without acquiring self._lock before validation.","commonSituations":"Custom Beam IO sources subclassing OffsetRangeTracker and reimplementing claim logic without proper locking; testing internal methods directly without the lock.","solutions":["Wrap the call in `with self._lock:` before invoking _validate_record_start.","Reuse try_claim(record_start) / set_current_position(...) instead of calling the private method directly — they acquire the lock correctly.","If subclassing, call super().try_claim(...) rather than reimplementing validation outside the lock."],"exampleFix":"// before\ntracker._validate_record_start(5, True)  # no lock held\n// after\nwith tracker._lock:\n    tracker._validate_record_start(5, True)","handlingStrategy":"validation","validationCode":"assert tracker._lock.locked(), \"_validate_record_start requires self._lock held\"","typeGuard":"def lock_held(tracker) -> bool:\n    return tracker._lock.locked()","tryCatchPattern":"try:\n    with tracker._lock:\n        tracker._validate_record_start(start, split_point)\nexcept ValueError as e:\n    logger.error(\"Tracker misuse: %s\", e)\n    raise","preventionTips":["Never call private tracker methods directly; use try_claim/set_current_position.","Always use `with self._lock:` when subclassing tracker internals.","Add lock-held assertions in custom source tests."],"tags":["python","apache-beam","range-tracker","threading","lock"],"backgroundTag":"internal-invariant-violation","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"}