Raise on length mismatch in batched IterableDataset.map - #8332
Merged
lhoestq merged 1 commit intoJul 22, 2026
Merged
Conversation
When a batched function passed to `IterableDataset.map` changes the number
of rows but does not re-emit every retained input column, the merge
`{**inputs, **processed_inputs}` in `MappedExamplesIterable._iter` combined
mismatched-length columns. Because `_batch_to_examples` derives the row count
from the first column in insertion order, this silently dropped or misaligned
rows, or raised a bare `IndexError`, depending on which column came first.
Eager `Dataset.map` raises `pyarrow.lib.ArrowInvalid` on the same input, so the
streaming path silently diverged from the eager API it is meant to mimic.
Validate, for batched maps, that every retained input column matches the
processed output length and raise a clear `ValueError` otherwise, reusing the
existing "Column lengths mismatch" wording from `validate_function_output`. The
check runs after the `remove_columns` deletion so that dropping input columns
while returning a new column at a different length keeps working.
Add tests covering the shrink, expand and new-shorter-column failure modes
(including parity with the eager `ArrowInvalid`) plus the legitimate
length-changing patterns that must keep passing.
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
IterableDataset.map(fn, batched=True)can silently drop or misalign rows when the mapping function changes the batch's row count but does not re-emit every retained input column.In
MappedExamplesIterable._iter,prepare_outputsmerges the batch withtransformed_inputs = {**inputs, **processed_inputs}without checking that the retained original input columns have the same length as the processed output.validate_function_outputonly checks length consistency withinprocessed_inputs, never against the untouched input columns._batch_to_examplesthen derives the row count from whichever column is first in insertion order (usually a retained input column atbatch_sizelength).The result depends on which column ends up first:
The eager
Dataset.mapraises a clear error on the identical input:So the streaming path silently diverges from the eager API it explicitly intends to mimic (the
# this logic mimics the one in Dataset.mapcomment right above the merge).Fix
In
prepare_outputs, after theremove_columnsdeletion and before the merge, validate for batched maps that every retained input column (a key ininputsnot overwritten byprocessed_inputs) matches the processed output length. If not, raise aValueErrorreusing the existing"Column lengths mismatch"wording fromvalidate_function_output, plus a hint.The check is placed after the
remove_columnsblock on purpose: a legitimate and already-tested pattern returns a new column at a length different from the batch while removing all input columns viaremove_columns(seetest_mapped_examples_iterable_remove_columns), which must keep working.All legitimate length-changing patterns are preserved: returning every column at a new consistent length (grow/shrink), same-length overwrite of an existing column, and adding a new column at batch length.
FilteredExamplesIterableinherits this path; correct batched filters return only the mask column at batch length and are unaffected, while a wrong-length mask now raises instead of silently misfiltering. Both the sync and async map paths funnel throughprepare_outputs, so one change covers both.Behavior change
Pipelines that today silently produce misaligned/truncated rows will now raise
ValueError— matching what eagerDataset.mapalready does withArrowInvalidon the same input.Testing
Added to
tests/test_iterable_dataset.py, next to the existing eager-vs-streaming parity test:test_iterable_dataset_map_batched_shrink_without_all_columns_raises— the shrink case must raise (and asserts eagerDataset.mapraisesArrowInvalidon the same input).test_iterable_dataset_map_batched_expand_without_all_columns_raises— the expand case must raiseValueErrorinstead of a bareIndexError.test_iterable_dataset_map_batched_new_shorter_column_raises— new-shorter-column must raiseValueErrorinstead of a bareIndexError.test_iterable_dataset_map_batched_length_change_valid_patterns— regression guard for the five legitimate patterns.