apache/beam · error · ValueError
Split result must be a tuple that contains split position an
Error message
Split result must be a tuple that contains split position and split fraction. Received: %r
What it means
Raised inside _assert_split_at_fraction_behavior (used by assert_split_at_fraction_behavior and assert_split_at_fraction_binary) when RangeTracker.try_split() returns a non-None result that is not a 2-element tuple of (split_position, split_fraction). A successful split must return exactly two values per the iobase.RangeTracker contract. This indicates a custom RangeTracker implementation violating the try_split return contract.
Source
Thrown at sdks/python/apache_beam/io/source_test_utils.py:303
stop_position=None):
range_tracker = source.get_range_tracker(start_position, stop_position)
assert isinstance(range_tracker, iobase.RangeTracker)
current_items = []
reader = source.read(range_tracker)
# Reading 'num_items_to_read_before_split' items.
reader_iter = iter(reader)
for _ in range(num_items_to_read_before_split):
current_items.append(next(reader_iter))
suggested_split_position = range_tracker.position_at_fraction(split_fraction)
stop_position_before_split = range_tracker.stop_position()
split_result = range_tracker.try_split(suggested_split_position)
if split_result is not None:
if len(split_result) != 2:
raise ValueError(
'Split result must be a tuple that contains split '
'position and split fraction. Received: %r' % (split_result, ))
if range_tracker.stop_position() != split_result[0]:
raise ValueError(
'After a successful split, the stop position of the '
'RangeTracker must be the same as the returned split '
'position. Observed %r and %r which are different.' %
(range_tracker.stop_position() % (split_result[0], )))
if split_fraction < 0 or split_fraction > 1:
raise ValueError(
'Split fraction must be within the range [0,1]',
'Observed split fraction was %r.' % (split_result[1], ))
stop_position_after_split = range_tracker.stop_position()
if split_result and stop_position_after_split == stop_position_before_split:
raise ValueError(View on GitHub (pinned to 12126d8942)
Solutions
- Return a 2-tuple (split_position, split_fraction) from try_split on success.
- Return None (not an empty tuple/list) when the split cannot be performed.
- Prefer subclassing apache_beam.io.iobase.RangeTracker, which enforces the contract.
- Log repr(split_result) (shown in the message) to see the actual returned structure.
Example fix
# before
def try_split(self, fraction):
return self.position_at_fraction(fraction)
# after
def try_split(self, fraction):
pos = self.position_at_fraction(fraction)
if pos is None:
return None
return (pos, fraction) Defensive patterns
Strategy: type-guard
Validate before calling
res = tracker.try_split(pos)
if res is not None and not (isinstance(res, tuple) and len(res) == 2):
raise TypeError("try_split must return (position, fraction) or None") Type guard
def is_valid_split_result(res):
return res is None or (isinstance(res, tuple) and len(res) == 2) Try / catch
try:
source_test_utils.assert_split_at_fraction_behavior(src, n, f, outcome)
except ValueError as e:
if "Split result must be a tuple" in str(e):
logger.error("bad try_split return: %s", e)
raise Prevention
- Subclass iobase.RangeTracker instead of reimplementing the interface
- Return exactly (position, fraction) on success and None on refusal
- Type-check try_split's return in your connector's own tests
When it happens
Trigger: A custom RangeTracker's try_split returns None-of-length-2 structures such as a single value, a 3-tuple, a list with extra items, or a non-sized object, while the test harness performs try_split after reading num_items_to_read_before_split items.
Common situations: Implementing a custom RangeTracker for a new connector and returning only the new position from try_split, or returning (position, fraction, old_stop).
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- After a successful split, the stop position of the RangeTrac
- Stop position %r did not change after a successful split of
- Expected split of source %r at fraction %r to be successful
- Error determining if %s allows dynamic splitting
- The first record [starting at %d] must be at a split point
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/7368042f72ac2e08.
Report an issue: GitHub.