Handling File Errors¶
File operations can fail for many reasons: the file may not exist, the user may lack permission, or the disk may be full. Robust programs anticipate these failures and handle them gracefully using Python's exception mechanism.
Mental Model
File errors are boundary failures---they happen where your program meets the outside world. The file might not exist, permissions might be wrong, or the disk might be full. Wrapping file operations in try/except blocks lets your program fail gracefully instead of crashing when the real world does not cooperate.
1. Common File Exceptions¶
Python raises specific exceptions for different file-related errors:
| Exception | When it occurs |
|---|---|
FileNotFoundError |
Opening a file that does not exist (read mode) |
PermissionError |
Insufficient permissions to read/write |
IsADirectoryError |
Attempting to open a directory as a file |
FileExistsError |
Creating a file that already exists (with 'x' mode) |
OSError |
General OS-level errors (disk full, bad path, etc.) |
All of these are subclasses of OSError, so catching OSError catches them all.
2. Basic try/except¶
The simplest pattern wraps the file operation in a try block and catches the
most likely exception:
python
try:
with open("data.txt") as f:
content = f.read()
print(content)
except FileNotFoundError:
print("Error: 'data.txt' was not found.")
The with statement ensures the file is closed even if an exception occurs inside
the block.
3. Catching Multiple Exceptions¶
When several failure modes are possible, catch each one separately to provide specific error messages:
python
try:
with open("data.txt") as f:
data = f.read()
except FileNotFoundError:
print("File does not exist.")
except PermissionError:
print("Permission denied. Check file permissions.")
except OSError as e:
print(f"OS error: {e}")
More specific exceptions should come before more general ones. If OSError were
listed first, it would catch FileNotFoundError and PermissionError before their
handlers could run.
4. The else and finally Clauses¶
The full try statement supports two additional clauses:
python
try:
f = open("data.txt")
except FileNotFoundError:
print("File not found.")
else:
# Runs only if no exception occurred
content = f.read()
print(f"Read {len(content)} characters.")
f.close()
finally:
# Always runs, whether or not an exception occurred
print("File operation attempted.")
else: runs when thetryblock completes without raising an exception. Use it to separate "success" logic from error handling.finally: runs unconditionally. It is typically used for cleanup, though thewithstatement handles file closing more cleanly.
5. Accessing Exception Details¶
Exception objects carry useful information. The as keyword binds the exception
to a variable:
python
try:
with open("/etc/shadow") as f:
f.read()
except PermissionError as e:
print(f"Error number: {e.errno}")
print(f"Message: {e.strerror}")
print(f"Filename: {e.filename}")
The attributes errno, strerror, and filename are available on all OSError
subclasses.
6. Raising Exceptions¶
Sometimes you want to validate a file before processing it and raise your own exception:
```python import os
def read_config(path): if not os.path.isfile(path): raise FileNotFoundError(f"Config file not found: {path}") with open(path) as f: return f.read() ```
Use raise to signal errors explicitly when a condition check fails.
7. Practical Pattern: Safe File Reading¶
A reusable function that reads a file with error handling:
python
def safe_read(path):
"""Read a file and return its contents, or None on failure."""
try:
with open(path) as f:
return f.read()
except FileNotFoundError:
print(f"File not found: {path}")
except PermissionError:
print(f"Permission denied: {path}")
except OSError as e:
print(f"Cannot read {path}: {e}")
return None
Summary¶
- File operations can raise
FileNotFoundError,PermissionError, and otherOSErrorsubclasses. - Use
try/exceptto handle errors; list specific exceptions before general ones. - The
elseclause runs on success;finallyruns unconditionally. - Use
as eto access details likee.errno,e.strerror, ande.filename. - The
withstatement ensures files are closed regardless of exceptions.
Notebook Examples¶
```python a = 3 b = 0
try: c = a / b except Exception as e: print(e) ```
```python a = 3 b = 0
try: c = a / b except ValueError as e: print(e) print("value error???") except ZeroDivisionError as e: # specific error print(e) print("I am dam stupid") except Exception as e: # general error print(e) print("I am done") ```
python
x = input("Number: ")
print(x, type(x))
python
try:
x = int(input("Number: "))
print(10 / x)
except ValueError:
print("Invalid number")
except ZeroDivisionError:
print("Cannot divide by zero")
python
try:
x = int(input("Number: "))
print(10 / x)
except ValueError:
print("Invalid number")
except ZeroDivisionError as e:
print(e)
print("Cannot divide by zero")
```python try: x = int(input("Number: ")) print(10 / x) except (ValueError, TypeError) as e: print(e) print("Invalid input")
except ZeroDivisionError as e:¶
print(e)¶
print("Cannot divide by zero")¶
except Exception as e: # general error¶
print(e)¶
print("I am done")¶
```
```python try: x = int(input("Number: ")) print(10 / x) except (ValueError, TypeError) as e: print(e) print("Invalid input")
except ZeroDivisionError as e:¶
print(e)¶
print("Cannot divide by zero")¶
except Exception as e: # general error print(e) print("I am done") ```
python
try:
x = int("10")
except ValueError:
print("Bad input")
except Exception as e: # general error
print(e)
print("I am done")
else: # no exception
print("Success")
python
try:
x = int("10")
except ValueError:
print("Bad input")
except Exception as e: # general error
print(e)
print("I am done")
else: # no exception
print("Success")
finally: # run with / without exception
print("runs no mater what")
```python def f(): raise ValueError("WOW") return 1
try: a = f() except ValueError as e: print(e) ```
Exercises¶
Exercise 1.
Write a function count_lines(path) that returns the number of lines in a file,
or \(-1\) if the file cannot be read.
Solution to Exercise 1
python
def count_lines(path):
"""Return the number of lines in path, or -1 on failure."""
try:
with open(path) as f:
return sum(1 for _ in f)
except OSError:
return -1
Exercise 2.
What is the output of the following code if missing.txt does not exist?
python
try:
with open("missing.txt") as f:
data = f.read()
except FileNotFoundError:
print("A")
else:
print("B")
finally:
print("C")
Solution to Exercise 2
Opening missing.txt raises FileNotFoundError. The except block prints A.
The else block is skipped (it only runs if no exception occurred). The finally
block always runs and prints C. Output:
text
A
C
Exercise 3.
Explain why catching the bare Exception class (or worse, BaseException) is
discouraged when handling file errors.
Solution to Exercise 3
Catching Exception intercepts all standard exceptions, including unrelated
ones like KeyboardInterrupt (via BaseException), TypeError, ValueError,
and bugs in the code itself. This masks real programming errors and makes
debugging difficult. Instead, catch the narrowest applicable exception (e.g.,
FileNotFoundError or OSError) so that unexpected errors propagate normally
and are noticed.
Exercise 4.
Write a function safe_write(path, text) that writes text to a file. If the
write fails, it should print an error message and return False; on success it
returns True.
Solution to Exercise 4
python
def safe_write(path, text):
"""Write text to path. Return True on success, False on failure."""
try:
with open(path, "w") as f:
f.write(text)
return True
except PermissionError:
print(f"Permission denied: {path}")
except OSError as e:
print(f"Cannot write to {path}: {e}")
return False