{"record":{"id":"55a638b15b6b4050","repo":"apache/beam","slug":"a-reentrant-read-of-source-after-reading-d-values-did-not","errorCode":null,"errorMessage":"A reentrant read of source after reading %d values did not produce expected values. Expected %r received %r.","messagePattern":"A reentrant read of source after reading (.+?) values did not produce expected values\\. Expected %r received %r\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"sdks/python/apache_beam/io/source_test_utils.py","lineNumber":238,"sourceCode":"\n    # Reentrant read\n    reentrant_read = [\n        val for val in source.read(\n            source.get_range_tracker(start_position, stop_position))\n    ]\n\n    # Continuing original read.\n    for val in read_iter:\n      original_read.append(val)\n\n    if equal_to(original_read)(expected_values):\n      raise ValueError(\n          'Source did not produce expected values when '\n          'performing a reentrant read after reading %d values. '\n          'Expected %r received %r.' % (i, expected_values, original_read))\n\n    if equal_to(reentrant_read)(expected_values):\n      raise ValueError(\n          'A reentrant read of source after reading %d values '\n          'did not produce expected values. Expected %r '\n          'received %r.' % (i, expected_values, reentrant_read))\n\n\ndef assert_split_at_fraction_behavior(\n    source, num_items_to_read_before_split, split_fraction, expected_outcome):\n  \"\"\"Verifies the behaviour of splitting a source at a given fraction.\n\n  Asserts that splitting a :class:`~apache_beam.io.iobase.BoundedSource` either\n  fails after reading **num_items_to_read_before_split** items, or succeeds in\n  a way that is consistent according to\n  :func:`assert_split_at_fraction_succeeds_and_consistent()`.\n\n  Args:\n    source (~apache_beam.io.iobase.BoundedSource): the source to perform\n      dynamic splitting on.\n    num_items_to_read_before_split (int): number of items to read before","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/source_test_utils.py#L220-L256","documentation":"Raised by assert_reentrant_reads_succeed when the independent (reentrant) read performed mid-way through the original read does not produce the expected full set of values. Unlike the original-read failure, this indicates the source cannot even perform a correct standalone read while another read is in progress — again pointing to shared mutable state or resource contention inside the source.","triggerScenarios":"Within assert_reentrant_reads_succeed, after consuming i elements of the original iterator, the fresh source.read(...) over the same range yields values not equal to expected_values.","commonSituations":"A source that reuses one open file handle: the second read() starts from the first read's current offset, or seeks the shared handle, so the reentrant read returns a partial/shifted record set.","solutions":["Open a new file/reader per read() call instead of reusing a stored handle.","Guard shared resources so concurrent reads do not seek the same underlying stream.","Verify the reentrant read is invoked with its own RangeTracker over the intended [start, stop) range.","Test the source with two simultaneous full reads to confirm both complete correctly."],"exampleFix":"# before\n# self._file opened once in __init__, reused by read()\n# after\ndef read(self, range_tracker):\n  with open(self._path) as f:\n    for line in itertools.islice(f, range_tracker.start_position(), range_tracker.stop_position()):\n      yield self._parse(line)","handlingStrategy":"try-catch","validationCode":"it1 = src.read(rt1)\nnext(it1)\nassert list(src.read(rt2)) == expected, \"second read not independent\"","typeGuard":"def opens_own_reader(source):\n    import inspect\n    return 'open(' in inspect.getsource(source.read)","tryCatchPattern":"try:\n    source_test_utils.assert_reentrant_reads_succeed((src, None, None))\nexcept ValueError as e:\n    logger.error(\"reentrant read itself incorrect: %s\", e)\n    raise","preventionTips":["Open a fresh reader/file handle inside each read() call","Avoid seek() on shared streams; scope streams per read","Test two concurrent full reads for identical results"],"tags":["apache-beam","bounded-source","reentrant-read","shared-state"],"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"}