Skip to main content

How to Resolve Python "ValueError: I/O operation on closed file"

The ValueError: I/O operation on closed file is a common Python error that occurs when you try to perform a read or write operation (like file.read(), file.write(), file.readlines()) on a file object that has already been closed. This often happens due to incorrect indentation when using the with open() statement or by trying to access the file after manually closing it.

This guide explains the common causes of this error and shows how to fix it by ensuring file operations happen only while the file is open.

Understanding the Error: Files Must Be Open for I/O

Before you can read data from or write data to a file on your disk, your Python program needs to establish an active connection to that file. The open() function does this, returning a file object (often called a file handle) that represents this connection. All read/write operations are performed through methods on this file object (e.g., my_file.write(...)).

Once the connection is no longer needed, the file should be closed (my_file.close()). Closing releases the file handle back to the operating system, ensures all buffered data is written to disk, and prevents further accidental operations.

The ValueError: I/O operation on closed file occurs when you try to call a method like .read() or .write() on a file object after it has been closed.

Cause 1: Incorrect Indentation with with open() (Most Common)

The with open(...) as ...: statement is the preferred way to handle files in Python because it automatically closes the file when the indented block underneath it is exited (either normally or due to an error). If code that needs to access the file object is mistakenly placed outside this indented block, it will run after the file has already been closed.

import os
filename = "my_data.txt"

# Error Scenario: Write operations outside the 'with' block's indentation
try:
with open(filename, 'w', encoding='utf-8') as my_file:
print("Inside 'with' block - writing first line.")
my_file.write('First line.\n')
# The 'with' block ends here due to indentation

# These lines are OUTSIDE the 'with' block
print("Outside 'with' block.")
print(f"Is file closed? {my_file.closed}") # Output: True

# ⛔️ ValueError: I/O operation on closed file.
# Trying to write to the file after the 'with' block closed it.
my_file.write('Second line.\n')
my_file.write('Third line.\n')

except ValueError as e:
print(f"Caught Error: {e}")
except Exception as e: # Catch other potential errors
print(f"Caught Unexpected Error: {e}")
finally:
if os.path.exists(filename): os.remove(filename) # Cleanup

Because the last two my_file.write() calls are not indented under the with statement, they execute after the with block has finished and automatically closed my_file.

Ensure that all code that needs to interact with the file object (my_file in the example) is correctly indented inside the with open() block.

import os
filename = "my_data_fixed.txt"

# Solution: Indent all file operations correctly
try:
with open(filename, 'w', encoding='utf-8') as my_file:
print("Inside 'with' block (Corrected).")
my_file.write('First line.\n')
print(f"Is file closed inside block? {my_file.closed}") # Output: False

# ✅ These operations are now correctly indented
my_file.write('Second line.\n')
my_file.write('Third line.\n')
print("Wrote second and third lines.")

# Code outside the block runs after the file is automatically closed
print("Outside 'with' block (Corrected).")
print(f"Is file closed outside block? {my_file.closed}") # Output: True

# Attempting to write here would still cause an error, which is expected.
# my_file.write('Fourth line.\n') # ValueError

except Exception as e:
print(f"An unexpected error occurred: {e}")
finally:
if os.path.exists(filename): os.remove(filename) # Cleanup

By indenting the write calls properly, they execute while the file handle my_file is still open and valid.

Cause 2: Mixing Tabs and Spaces (Indentation Issues)

Python relies heavily on consistent indentation. Mixing tab characters and space characters for indentation within the same file (or even the same block) can confuse the interpreter about which lines belong inside the with block. A line might look indented correctly in your editor but might actually be considered outside the block by Python if its whitespace doesn't match the surrounding lines (e.g., using spaces when the with line used tabs, or vice versa).

  • Configure Your Editor: Set your code editor to use spaces for indentation (typically 4 spaces per level, as recommended by PEP 8) and potentially enable the "convert tabs to spaces" option.
  • Visualize Whitespace: Enable the "render whitespace" or "show invisibles" feature in your editor to see exactly where tabs () and spaces (·) are being used.
  • Fix Inconsistencies: Manually correct lines with mixed indentation or use your editor's "Untabify" or "Convert Indentation to Spaces" commands.

Cause 3: Accessing File After Manual close()

If you are using the open() function directly without a with statement, the error occurs if you try to use the file object after you have explicitly called my_file.close().

import os
filename = "manual_close.txt"
my_file = None # Initialize

# Error Scenario: Accessing after manual close()
try:
my_file = open(filename, 'w', encoding='utf-8')
print("File opened manually.")
my_file.write("Data written while open.\n")

print(f"Is file closed before close()? {my_file.closed}") # Output: False
my_file.close() # Manually close the file
print(f"Is file closed after close()? {my_file.closed}") # Output: True

# ⛔️ ValueError: I/O operation on closed file.
my_file.write("Trying to write again.\n")

except ValueError as e:
print(f"Caught Error: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
finally:
# Ensure cleanup even if close failed or wasn't reached
if my_file and not my_file.closed: my_file.close()
if os.path.exists(filename): os.remove(filename)

Solution 3: Perform All Operations Before Manual close()

When managing files manually with open(), ensure all read/write operations are completed before you call my_file.close(). Using a try...finally block is recommended to guarantee the file is closed even if errors occur.

import os

filename = "manual_close_fixed.txt"
my_file = None # Initialize

try:
my_file = open(filename, 'w', encoding='utf-8')
print("File opened manually (Corrected).")
# ✅ Perform all operations before close()
my_file.write("Data written while open 1.\n")
my_file.write("Data written while open 2.\n")
print("Writing complete.")
except Exception as e:
print(f"An error occurred during I/O: {e}")
finally:
# ✅ Close the file in the 'finally' block to ensure it happens
if my_file: # Check if file was successfully opened
my_file.close()
print("File closed in finally block.")
if os.path.exists(filename): os.remove(filename) # Cleanup

However, using with open() (Solution 1) is strongly preferred as it handles closing automatically and more reliably.

Checking if a File is Closed (file.closed)

You can check the status of a file object using its .closed attribute, which returns True if the file is closed and False otherwise.

filename = "check_closed.txt"

with open(filename, 'w', encoding='utf-8') as f:
print(f"Inside 'with', f.closed: {f.closed}") # Output: False
f.write("test")

print(f"Outside 'with', f.closed: {f.closed}") # Output: True

if f.closed:
print("File is confirmed closed.")
else:
print("File is confirmed open.")

if os.path.exists(filename):
os.remove(filename)

This is useful for debugging or conditional logic, but doesn't fix the error itself.

Example with CSV Files

The same error occurs if you try to use a csv.writer or csv.reader object associated with a file handle after that file handle has been closed (e.g., outside the with block).

Example with Error:

import csv
import os

filename = "temp.csv"

# Error Scenario: CSV writing outside 'with' block
try:
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['col_a', 'col_b']
writer = csv.writer(csvfile)
print("Inside 'with' for CSV.")
# Correct place to write header/rows would be here

# ⛔️ These lines are outside the 'with' block; csvfile is closed.
print(f"CSV file closed? {csvfile.closed}") # Output: True
# ⛔️ ValueError: I/O operation on closed file.
writer.writerow(['val1', 'val2']) # writer tries to use closed csvfile
print("Wrote row outside block (should fail).")

except ValueError as e:
print(f"Caught CSV Error: {e}")
finally:
if os.path.exists(filename): os.remove(filename)

Correct Solution:

import csv
import os

filename = "temp.csv"
# Solution: Indent CSV operations correctly
try:
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['col_a', 'col_b']
writer = csv.writer(csvfile)
print("Inside 'with' for CSV (Corrected).")
print(f"CSV file closed inside? {csvfile.closed}")

# ✅ Correctly indented CSV operations
writer.writerow(fieldnames) # Write header
writer.writerow(['data1', 'data2']) # Write row
print("CSV writing done inside block.")

print(f"CSV file closed outside? {csvfile.closed}")

except Exception as e:
print(f"An unexpected error occurred: {e}")
finally:
if os.path.exists(filename): os.remove(filename)

Conclusion

The ValueError: I/O operation on closed file means you're trying to read or write using a file object that is no longer connected to the actual file.

  • When using with open(...), ensure all file operations (.read(), .write(), using csv.reader/writer, etc.) are correctly indented within the with block. Avoid inconsistent indentation (mixing tabs/spaces).
  • When using manual open(), perform all operations before calling file.close(). Use try...finally to guarantee closure, but prefer the with statement.

Checking indentation and ensuring operations occur within the scope where the file is guaranteed to be open are the keys to resolving this error.