Handle File Errors in Python

Learn how to handle common Python file errors like FileNotFoundError and PermissionError, and write file handling code that fails gracefully instead of crashing.

5 min read

No matter how carefully you construct your file paths and choose your file modes, things will go wrong. A file your program expects to find might have been moved or deleted. A directory you need to write into might be read-only. A text file might contain bytes that are not valid in the encoding you specified. Python file errors are not signs that you wrote bad code; they are signs that the outside world, the file system, other programs, and the users who interact with your files, does not always behave the way your program expects. Handling these errors gracefully is what separates a script that crashes with an intimidating traceback from a program that reports the problem clearly and gives the user a path forward.

Python raises specific exception types for different categories of file errors. FileNotFoundError means the path you provided does not point to an existing file. PermissionError means the file exists but your program does not have the operating system rights to access it. IsADirectoryError means you tried to open a directory as if it were a file. UnicodeDecodeError means the bytes on disk cannot be interpreted with the encoding you specified. Each of these exceptions inherits from OSError, which means you can catch them individually for fine-grained handling or catch OSError as a broad safety net when the specific cause does not matter. This article covers the most common Python file errors, shows you how to handle each one, and gives you patterns for writing file handling code that fails safely.

Handling FileNotFoundError

FileNotFoundError is the most frequent file error you will encounter, and it almost always means one of two things: the file genuinely does not exist, or the path you provided is wrong. When your program tries to open a file in read mode or read-write mode and the file is missing, Python raises FileNotFoundError immediately. The fix depends on whether the file is required or optional. For a required configuration file, the correct response is to report the problem and exit. For an optional cache file, the correct response is to create it or skip the operation.

pythonpython
from pathlib import Path
 
config_path = Path("config.json")
 
try:
    with open(config_path, "r", encoding="utf-8") as file:
        settings = file.read()
except FileNotFoundError:
    print(f"Configuration file {config_path} not found.")
    settings = "{}"

The try-except block wraps the open call and catches FileNotFoundError specifically. If the configuration file is missing, the program prints a message and falls back to an empty JSON object. If the file exists and is readable, the read proceeds normally. This pattern replaces a crash with a controlled fallback, and it works the same way for any file your program opens that might legitimately be absent.

A subtle point about FileNotFoundError is that open with write mode or append mode does not raise it. Write and append modes create the file if it does not exist, so a missing file is not an error condition for those modes. If you need to ensure a file exists before reading but do not care about race conditions, you can check with Path.exists before opening. However, the file might be deleted between the check and the open, so catching the exception is the more robust approach.

Dealing with PermissionError

PermissionError occurs when your program attempts to read or write a file that the operating system blocks it from accessing. The file exists and the path is correct, but your user account lacks the necessary read or write permissions. PermissionError also appears when you try to write to a directory that is read-only, or when you try to open a file that another program has locked for exclusive access.

pythonpython
try:
    with open("/etc/protected.conf", "r", encoding="utf-8") as file:
        content = file.read()
except PermissionError:
    print("Cannot read the file. Check permissions or run with elevated access.")

On macOS and Linux, file permissions are controlled by the chmod command and visible with ls -l. A file owned by root with read permission only for the owner will raise PermissionError when your regular user account tries to open it. On Windows, file locking is more common. An Excel spreadsheet that is open in Microsoft Excel is often locked and cannot be opened by a Python script until Excel closes it. The solution for PermissionError depends on the context: change the file permissions, close the program that has it locked, or run your Python script with elevated privileges.

When writing, PermissionError can also indicate that the directory containing the file is read-only. Even if the file itself does not exist yet, creating a new file requires write permission on the directory that will contain it. If your script generates output in a system directory like /usr/local or C:\Program Files, it will likely fail with PermissionError unless run as an administrator. Writing to a location inside the user's home directory or the project folder avoids this class of permission problems entirely.

Catching encoding errors and other file exceptions

UnicodeDecodeError is not a subclass of OSError, but it is closely related to file handling because it fires when you open a text file with the wrong encoding. If a file contains bytes that are not valid in the encoding you specified, Python raises UnicodeDecodeError on the read call. The fix is to determine the file's actual encoding, which might be UTF-16, Latin-1, or a locale-specific encoding, and pass that encoding to open. For files of unknown encoding, you can open in binary mode to inspect the raw bytes, or pass errors="replace" to open to replace undecodable bytes with a replacement character instead of crashing.

IsADirectoryError occurs when you pass the path of a directory to open with a read or write mode. Directories cannot be opened as files, and Python detects this and raises the error immediately. The fix is to check whether a path points to a directory with Path.is_dir or os.path.isdir before attempting to open it.

NotADirectoryError is the opposite case: you provided a path where one of the intermediate components exists but is not a directory. For example, if the path is /home/user/data/file.txt and /home/user/data exists as a regular file rather than a directory, Python cannot create file.txt inside it. This error is rarer but worth knowing about because the fix is usually to rename or remove the non-directory that is blocking the path.

pythonpython
from pathlib import Path
 
output = Path("results") / "summary.txt"
output.parent.mkdir(parents=True, exist_ok=True)
 
with open(output, "w", encoding="utf-8") as file:
    file.write("Analysis complete.\n")

This pattern avoids both FileNotFoundError for missing parent directories and NotADirectoryError for paths where an intermediate component is wrong. The mkdir call creates the full directory tree if it does not exist, and the exist_ok argument prevents an error if the directory already exists. Placing this guard before the open call eliminates an entire category of file path errors with a single line.

Building resilient file handling routines

The with statement and try-except work together to create file handling code that is both safe and resilient. The with statement guarantees that open files are closed even when exceptions fire, and the try-except block decides what your program does in response to those exceptions. Wrapping the entire with block in a try-except is the standard pattern for any file operation where the file's existence or accessibility is uncertain.

When you design a function that works with files, decide early whether the function should handle its own errors or propagate them to the caller. A low-level function that reads a data file might return None or an empty list on FileNotFoundError, letting the caller decide whether a missing file is acceptable. A high-level function that writes a report might catch PermissionError, log the problem, and notify the user rather than crashing. The right choice depends on the role of the function in your program, but the mechanism is the same in every case: catch the specific exception, respond appropriately, and let the with statement handle the cleanup.

If you have read the earlier articles in this section, you now have a complete set of file handling skills. You can open files with the correct mode, read and write data efficiently, work with file paths, use the with statement for automatic cleanup, and handle the errors that inevitably arise when your code interacts with a real file system. The next article moves into strategies for processing large files without exhausting memory, building on the error handling patterns you have learned here.

Rune AI

Rune AI

Key Insights

  • FileNotFoundError signals a missing file; catch it to provide a fallback or a helpful message.
  • PermissionError means the OS denied access; check file locks, permissions, and whether the path is a directory.
  • Use try-except around open calls when the file's existence or accessibility is uncertain at runtime.
  • UnicodeDecodeError occurs when reading a file with the wrong encoding; try a different encoding or open in binary mode.
  • The with statement ensures files close even when exceptions occur, so pair with and try-except for resilient code.
RunePowered by Rune AI

Frequently Asked Questions

What is the most common file error in Python?

FileNotFoundError is the most common. It occurs when you try to open a file in read mode and the file does not exist at the specified path. Always check whether a file exists before opening it for reading, or wrap the open call in a try-except block to handle the missing file gracefully.

How do I check if a file exists before opening it?

Use Path('filename').exists() from the pathlib module, or os.path.exists('filename') from the os.path module. However, checking and then opening introduces a race condition: the file could be deleted between the check and the open. For most scripts, catching the FileNotFoundError with try-except is more robust.

What should I do when I get a PermissionError?

PermissionError means your program does not have the operating system permissions to read or write the file. Check that the file is not open in another program, that the path is a file and not a directory, and that your user has the appropriate read or write permissions. On macOS and Linux, the ls -l command shows permissions; on Windows, check the file properties.

Conclusion

Python file errors are predictable and handleable. FileNotFoundError, PermissionError, IsADirectoryError, and UnicodeDecodeError each have a specific cause and a clear fix. Wrapping file operations in try-except blocks lets your programs respond gracefully to missing files, permission problems, and corrupted data instead of crashing with a traceback. The with statement handles resource cleanup automatically, so your error handling code can focus on the logic of what to do when things go wrong.