apache/beam · error · IOError
Stream is closed.
Error message
Stream is closed.
What it means
_check_open() raises IOError('Stream is closed.') when any operation (tell, seek, read, readline, etc.) is attempted on a FilesystemIO stream whose closed flag is set. Once close() has been called the stream no longer fronts a valid downloader/reader, so every subsequent operation is rejected.
Solutions
- Reopen the file with FileSystems.open(path) before performing further operations.
- Track the closed state (f.closed) and guard read/tell calls, or keep the stream inside the `with` block that owns its lifetime.
- Remove duplicate close() calls so the stream isn't closed while still in use.
- Capture any data you need before the `with` block exits rather than deferring reads.
Example fix
// before
with FileSystems.open(path) as f:
pass
pos = f.tell() # IOError: Stream is closed.
// after
with FileSystems.open(path) as f:
pos = f.tell()
data = f.read() Defensive patterns
Strategy: validation
Validate before calling
if f.closed:
f = FileSystems.open(path) # reopen before further ops Type guard
def is_open(f) -> bool:
return not getattr(f, 'closed', True) Try / catch
try:
pos = f.tell()
except IOError as e:
if str(e) == 'Stream is closed.':
f = FileSystems.open(path)
pos = f.tell()
else:
raise Prevention
- Perform all reads/tells inside the `with FileSystems.open(...)` block.
- Check f.closed before any late/deferred access to a stream.
- Never call close() twice; centralize cleanup in one place.
- Don't return streams from context managers — return the data instead.
When it happens
Trigger: Calling f.tell(), f.seek(...), or f.read(...) after f.close(); using a stream inside a `with` block after the block has exited; holding a reference to a stream whose underlying context manager closed it.
Common situations: Returning file-like objects from context managers and using them later; accidentally calling close() twice in cleanup code then reading again; iterator/generator code that outlives the `with FileSystems.open(...)` scope.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- A schema is required to write non-schema'd data.
- A sink must inherit iobase.Sink, iobase.NativeSink, or be a…
- An explicit schema is required to write non-schema'd…
- At least one owner must be registered.
- AvroRowWriter is not readable
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/b2a01fbcd2e230fa.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/python/apache_beam/io/filesystemio.py:305
# Certain upload stream implementations seek to the end of a stream to check
# length before completing an upload, so we support a no-op seek(0, SEEK_END).
if whence == os.SEEK_END and offset == 0:
return
elif whence == os.SEEK_SET:
if offset == self.position:
return
elif offset == self.last_block_position and self.last_block:
self.position = offset
self.remaining = b''.join([self.last_block, self.remaining])
self.last_block = b''
return
raise NotImplementedError(
'offset: %s, whence: %s, position: %s, last: %s' %
(offset, whence, self.position, self.last_block_position))
def _check_open(self):
if self.closed:
raise IOError('Stream is closed.')
View on GitHub (pinned to 12126d8942)