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.

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.

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.

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.

Quick checklist
- Keep reads and writes inside the
with open(...)block. - Do not call
close()manually inside awithblock. - For CSV files, iterate or materialize rows before the file closes.
- For
BytesIOandStringIO, read the value before closing the stream. - If you need to use a file later, reopen it in the correct mode.
Related Python guides
- Python open()
- Read file line by line in Python
- Python csv.DictReader
- Read CSV with NumPy
- Python StringIO
- ValueError: too many values to unpack
Official references
- Python open() documentation
- Python io module documentation
- Python csv module documentation
- Python ValueError documentation
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)

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.