Fix ValueError: I/O Operation on Closed File in Python

Quick answer: The closed-file error means a read, write, or seek happens after the file object’s lifetime ended. Keep operations inside the with block, or return the data that was read rather than a handle the function has already closed.

Python closed file infographic comparing with block ownership, returned content, closed handles, and explicit lifecycle
The with block owns the file lifetime; finish reads and writes before leaving it, or return data rather than the closed handle.

ValueError: I/O operation on closed file means Python tried to read from or write to a file-like object after it was closed. Once a file handle is closed, operations such as read(), write(), seek(), or CSV iteration can no longer use that handle.

The usual fix is to keep all file operations inside the active with open(...) block, or reopen the file when you need to use it again.

Why the error happens

A file object owns an operating-system resource. Calling close() releases that resource. A context manager does the same thing automatically when the with block ends.

file = open("notes.txt", "w", encoding="utf-8")
file.write("first line\n")
file.close()
file.write("second line\n")

The final line raises ValueError: I/O operation on closed file. because the handle has already been closed.

Fix with a context manager

with open("notes.txt", "w", encoding="utf-8") as file:
    file.write("first line\n")
    file.write("second line\n")

Both writes happen while the file is open. Python closes the file automatically after the indented block finishes.

Python Pool infographic showing file object, with block exit, closed state, later read, and ValueError
A file opened by a with block is closed automatically when execution leaves that block.

Check indentation after with open

A common mistake is writing code after the with block when it still needs the file handle:

with open("notes.txt", "w", encoding="utf-8") as file:
    file.write("first line\n")

file.write("second line\n")  # ValueError

Move the second write inside the block, or open the file again in append mode:

with open("notes.txt", "a", encoding="utf-8") as file:
    file.write("second line\n")

CSV reader closed-file error

csv.reader and csv.DictReader read from the file object you pass in. If you create the reader inside a with block but iterate over it after the block ends, the underlying file is closed.

import csv

with open("grades.csv", newline="", encoding="utf-8") as file:
    rows = list(csv.DictReader(file))

print(rows)

Converting to list inside the block reads the data before the file closes. For large files, process each row inside the block instead of storing every row.

BytesIO and StringIO

The same rule applies to in-memory streams such as io.BytesIO and io.StringIO. They are file-like objects, and closing them prevents later operations.

from io import BytesIO

buffer = BytesIO()
buffer.write(b"abc")
buffer.seek(0)
print(buffer.read())
buffer.close()

Call getvalue(), read(), or seek() before closing the buffer if you still need the content.

Python Pool infographic showing open file, with block, read or write operation, cleanup, and valid scope
Perform file I/O while the context is active and return data rather than the closed file object when needed.

Reopen the file when you need another operation

If you need to write first and read later, use two separate with open() blocks. That makes the lifetime of each file handle clear and prevents accidental use after close.

with open("notes.txt", "w", encoding="utf-8") as file:
    file.write("first line\n")

with open("notes.txt", "r", encoding="utf-8") as file:
    text = file.read()

print(text)

This is better than trying to keep one handle alive across unrelated parts of a program. It also makes file mode explicit: write with "w", append with "a", and read with "r".

Return data, not a closed file handle

Another common source of this error is returning a file object from inside a helper function after the context manager has already closed it.

def load_text(path):
    with open(path, "r", encoding="utf-8") as file:
        return file.read()

Return the data you need, such as a string, bytes, or parsed rows. Do not return the file handle unless the caller is responsible for opening and closing it.

Python Pool infographic mapping file contents through read, stored variable, context exit, and usable data
Read or copy the required data before leaving the context if later processing does not need the live file.

Quick checklist

  • Keep reads and writes inside the with open(...) block.
  • Do not call close() manually inside a with block.
  • For CSV files, iterate or materialize rows before the file closes.
  • For BytesIO and StringIO, read the value before closing the stream.
  • If you need to use a file later, reopen it in the correct mode.

Related Python guides

Official references

Conclusion

To fix ValueError: I/O operation on closed file, move the file operation back inside the active with open() block or reopen the file before using it again. The same principle applies to normal files, CSV readers, BytesIO, and StringIO.

Keep Work Inside with

The with statement closes the file when its indented suite exits, including when an exception occurs. Perform all reads and writes inside that suite. The file variable may still exist afterward, but it refers to a closed resource.

from pathlib import Path

path = Path("notes.txt")
with path.open("w", encoding="utf-8") as handle:
    handle.write("Python Pool\n")

with path.open(encoding="utf-8") as handle:
    content = handle.read()
print(content)
Python Pool infographic testing file.closed, nested contexts, flush, binary mode, and validation
Check lifecycle scope, flush and close behavior, text versus binary mode, and whether another function closes the handle.

Return Content, Not An Owned Handle

When a helper opens a file itself, the helper should normally return text, bytes, or a parsed object. Returning the closed handle creates a lifetime mismatch for the caller. If streaming is required, accept an already-open handle or make ownership explicit in the API.

def read_text(path):
    with open(path, encoding="utf-8") as handle:
        return handle.read()

text = read_text("notes.txt")
print(text)

Diagnose Ownership Before Reopening

file.closed is useful for confirming the symptom, but reopening blindly can hide a design bug or lose the intended file position. Check which function owns the handle, where the with block ends, and whether a callback is being invoked after the resource has been released.

with open("notes.txt", encoding="utf-8") as handle:
    print(handle.closed)
print(handle.closed)

Python’s with-statement reference explains the resource-lifetime boundary.

For related file boundaries, continue with reading line by line, writing bytes, and copying a file.

Frequently Asked Questions

Why does Python say I/O operation on closed file?

The program is trying to read, write, or seek after the file object has been closed.

How do I keep a file open while reading?

Perform the file operations inside the with open(…) block that owns the handle.

Can I return a file handle from a function?

You can, but the caller must own a still-open resource; returning the read content is often safer when the function opens the file itself.

How do I check whether a file is closed?

Use file.closed for diagnostics, but fix the ownership and lifetime design rather than reopening handles blindly.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted