{"record":{"id":"5071ecc81dff0f13","repo":"apache/beam","slug":"expected-split-of-source-r-at-fraction-r-to-be-successful","errorCode":null,"errorMessage":"Expected split of source %r at fraction %r to be successful after reading %d elements. But the split failed.","messagePattern":"Expected split of source %r at fraction %r to be successful after reading (.+?) elements\\. But the split failed\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/source_test_utils.py","lineNumber":328,"sourceCode":"          'RangeTracker must be the same as the returned split '\n          'position. Observed %r and %r which are different.' %\n          (range_tracker.stop_position() % (split_result[0], )))\n\n    if split_fraction < 0 or split_fraction > 1:\n      raise ValueError(\n          'Split fraction must be within the range [0,1]',\n          'Observed split fraction was %r.' % (split_result[1], ))\n\n  stop_position_after_split = range_tracker.stop_position()\n  if split_result and stop_position_after_split == stop_position_before_split:\n    raise ValueError(\n        'Stop position %r did not change after a successful '\n        'split of source %r at fraction %r.' %\n        (stop_position_before_split, source, split_fraction))\n\n  if expected_outcome == ExpectedSplitOutcome.MUST_SUCCEED_AND_BE_CONSISTENT:\n    if not split_result:\n      raise ValueError(\n          'Expected split of source %r at fraction %r to be '\n          'successful after reading %d elements. But '\n          'the split failed.' %\n          (source, split_fraction, num_items_to_read_before_split))\n  elif expected_outcome == ExpectedSplitOutcome.MUST_FAIL:\n    if split_result:\n      raise ValueError(\n          'Expected split of source %r at fraction %r after '\n          'reading %d elements to fail. But splitting '\n          'succeeded with result %r.' % (\n              source,\n              split_fraction,\n              num_items_to_read_before_split,\n              split_result))\n\n  elif (expected_outcome\n        != ExpectedSplitOutcome.MUST_BE_CONSISTENT_IF_SUCCEEDS):\n    raise ValueError('Unknown type of expected outcome: %r' % expected_outcome)","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/source_test_utils.py#L310-L346","documentation":"Raised by _assert_split_at_fraction_behavior when the test expects the split to succeed (ExpectedSplitOutcome.MUST_SUCCEED_AND_BE_CONSISTENT) but RangeTracker.try_split returned None after reading num_items_to_read_before_split elements at the requested fraction. This means the source/RangeTracker refused a dynamic split that Beam's harness expects to be possible, defeating work rebalancing. It is an expectation mismatch between the test's declared outcome and the tracker's actual behavior.","triggerScenarios":"assert_split_at_fraction_behavior(source, n, fraction, ExpectedSplitOutcome.MUST_SUCCEED_AND_BE_CONSISTENT) where the source's RangeTracker returns None from try_split at that point — e.g. a tracker that never supports splitting, or rejects splits too close to start/stop boundaries.","commonSituations":"Custom sources that conservatively return None from try_split (unsplittable), split_fraction chosen where remaining range is too small, or sources whose RangeTracker disables splitting below a size threshold while the test asserts success.","solutions":["Implement try_split in the source's RangeTracker so it actually splits at the suggested position instead of always returning None.","If the source legitimately cannot split, change the test expectation to ExpectedSplitOutcome.MUST_FAIL or DONOT_KNOW.","Verify the chosen split_fraction leaves a non-empty remainder range (avoid splitting at ~0.0 or ~1.0 boundaries that the tracker rejects).","Ensure position_at_fraction maps the fraction to a valid position inside [start, stop) so the split isn't declined as out-of-range."],"exampleFix":"// before: always refuse split\ndef try_split(self, fraction):\n  return None\n// after\ndef try_split(self, fraction):\n  pos = self.position_at_fraction(fraction)\n  if pos is None or pos <= self.start_position() or pos >= self.stop_position():\n    return None\n  self._stop = pos\n  return (pos, fraction)","handlingStrategy":"validation","validationCode":"pos = tracker.position_at_fraction(f)\nwill_split = tracker.try_split(pos) is not None\nif expected_outcome == ExpectedSplitOutcome.MUST_SUCCEED_AND_BE_CONSISTENT and not will_split:\n    pytest.fail(\"tracker refuses expected split at fraction %r\" % f)","typeGuard":"def supports_splitting(source):\n    return not type(source.get_range_tracker(None, None)).try_split.__qualname__.startswith(\"RangeTracker\") or True","tryCatchPattern":"try:\n    source_test_utils.assert_split_at_fraction_behavior(src, n, f, outcome)\nexcept ValueError as e:\n    if \"to be successful\" in str(e) or \"the split failed\" in str(e):\n        logger.error(\"tracker declined expected split: %s\", e)\n    raise","preventionTips":["Implement try_split rather than unconditionally returning None","Match test expectations (MUST_FAIL vs MUST_SUCCEED) to actual tracker capability","Avoid split fractions at range boundaries the tracker legitimately rejects","Ensure position_at_fraction yields an interior position of [start, stop)"],"tags":["apache-beam","range-tracker","dynamic-splitting","expected-outcome-mismatch"],"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-14T11:17:12.474Z"}