Python: Check if File or Directory Exists
The quickest way to check if a file exists in Python is os.path.exists(path) — it returns True if the path exists (file or directory) and False otherwise. For modern Python 3.4+ code, Path("file.txt").exists() from pathlib is the preferred approach.
This guide covers every reliable method, when to use each one, and the common mistakes developers make.
Quick Answer
import os
if os.path.exists("file.txt"):
print("File exists")
else:
print("File does not exist")
Or with pathlib (Python 3.4+):
from pathlib import Path
if Path("file.txt").exists():
print("File exists")
Method 1: os.path.exists()
os.path.exists(path) returns True if the path refers to an existing file or directory. It returns False for broken symbolic links.
import os
path = "/tmp/data.csv"
if os.path.exists(path):
print(f"{path} exists")
else:
print(f"{path} does not exist")
When to use it: When you need a quick existence check and you don't care whether the path is a file or a directory.
Caveat — race conditions: A gap exists between the time you check and the time you use the file (TOCTOU — Time Of Check To Time Of Use). Another process could delete the file between the exists() call and your open() call. For race-safe file access, use the try/except pattern described in Method 5.
Method 2: os.path.isfile()
os.path.isfile(path) returns True only if the path is an existing regular file — not a directory, socket, or device.
import os
path = "report.txt"
if os.path.isfile(path):
print("It's a file — safe to open")
else:
print("Not a file (might be a directory, or doesn't exist)")
When to use it: When you specifically need to confirm you have a regular file before reading or writing it. Using isfile() instead of exists() prevents accidentally treating a directory as a readable file.
Method 3: os.path.isdir()
os.path.isdir(path) returns True only if the path is an existing directory.
import os
path = "/home/user/documents"
if os.path.isdir(path):
print("Directory exists")
else:
print("Not a directory (might be a file, or doesn't exist)")
When to use it: Before creating files inside a directory, or when validating that a configuration path points to a directory rather than a file.
import os
output_dir = "results"
if not os.path.isdir(output_dir):
os.makedirs(output_dir)
Method 4: pathlib Path.exists(), is_file(), is_dir()
pathlib was introduced in Python 3.4 and is now the recommended way to work with filesystem paths. It provides an object-oriented interface that is more readable and cross-platform than string-based os.path calls.
Path.exists()
from pathlib import Path
p = Path("config.json")
if p.exists():
print("Path exists")
Path.is_file()
from pathlib import Path
p = Path("config.json")
if p.is_file():
print("It's a regular file")
Path.is_dir()
from pathlib import Path
p = Path("/var/log")
if p.is_dir():
print("It's a directory")
Combining pathlib with file reading
from pathlib import Path
config_path = Path("settings") / "config.json"
if config_path.is_file():
content = config_path.read_text(encoding="utf-8")
print(content)
else:
print("Config file not found")
The / operator on Path objects builds paths safely across operating systems — no need to worry about forward vs. back slashes.
When to use it: New projects and any code targeting Python 3.4+. pathlib is cleaner, more expressive, and handles path joining and manipulation better than os.path.
Method 5: try/except FileNotFoundError
Python's EAFP principle — Easier to Ask Forgiveness than Permission — says it is often better to attempt an operation and handle the exception than to check for a condition first. For file access, this approach is also race-condition safe.
try:
with open("data.txt", "r") as f:
content = f.read()
print(content)
except FileNotFoundError:
print("File not found")
except PermissionError:
print("Permission denied")
Why this is race-safe: You never check separately — you just try to open the file. If it disappears between a check and an open, you still get a clean exception rather than a crash or undefined behaviour.
Catching OSError for broader coverage:
try:
with open("data.txt", "r") as f:
content = f.read()
except OSError as e:
print(f"Could not read file: {e}")
FileNotFoundError is a subclass of OSError, so catching OSError also handles permission errors and other I/O failures in one block.
When to use it: Whenever you are going to open and use the file immediately after checking — which is almost always. This is the most Pythonic pattern.
Comparison Table
| Method | Checks | Files | Dirs | Race-safe | Python version |
|---|---|---|---|---|---|
os.path.exists() | File or directory | Yes | Yes | No | 2.x+ |
os.path.isfile() | Files only | Yes | No | No | 2.x+ |
os.path.isdir() | Directories only | No | Yes | No | 2.x+ |
Path.exists() | File or directory | Yes | Yes | No | 3.4+ |
Path.is_file() | Files only | Yes | No | No | 3.4+ |
Path.is_dir() | Directories only | No | Yes | No | 3.4+ |
try/except FileNotFoundError | File (at open time) | Yes | No | Yes | 2.x+ |
When to Use Which
Use os.path.exists() when you need a simple existence check and the result won't be immediately followed by file I/O. Example: validating user input before queuing a background job.
Use os.path.isfile() when you must confirm the path is a regular file — not a directory — before processing it. Example: checking a log file path before rotating it.
Use os.path.isdir() when you need to confirm a directory exists before writing output files into it.
Use pathlib for all new Python 3 code. It is cleaner, supports the / operator for path joining, and has equivalent exists(), is_file(), and is_dir() methods.
Use try/except FileNotFoundError whenever you are going to open the file right after checking. This is the safest and most Pythonic pattern — it eliminates the race window and handles the error in one place.
Common Mistakes
Mistake 1: Using exists() when you mean isfile()
# Risky — path could be a directory, not a file
if os.path.exists("output"):
with open("output", "r") as f: # IsADirectoryError if "output" is a dir
data = f.read()
# Better — confirm it is actually a file
if os.path.isfile("output"):
with open("output", "r") as f:
data = f.read()
Mistake 2: Building paths with string concatenation instead of os.path.join or pathlib
# Wrong — breaks on Windows, fragile with trailing slashes
path = base_dir + "/" + filename
# Correct with os.path
import os
path = os.path.join(base_dir, filename)
# Correct with pathlib
from pathlib import Path
path = Path(base_dir) / filename
String concatenation produces paths that break on Windows (where the separator is \) and fails silently when base_dir already ends with a slash — or doesn't.
Mistake 3: Checking existence then opening without handling the race
# Race-prone — file could be deleted between check and open
if os.path.isfile("data.csv"):
with open("data.csv") as f: # could still raise FileNotFoundError
process(f)
# Race-safe
try:
with open("data.csv") as f:
process(f)
except FileNotFoundError:
print("data.csv was not found")
Summary
os.path.exists(path)— quick check for files or directories; not race-safe.os.path.isfile(path)— confirms the path is a regular file.os.path.isdir(path)— confirms the path is a directory.Path.exists()/Path.is_file()/Path.is_dir()— modernpathlibequivalents, preferred for Python 3.4+.try/except FileNotFoundError— the Pythonic, race-safe way to check existence right before using a file.
For most real-world code, combine pathlib for path construction with try/except for file access — that gives you clean, readable, and race-safe file handling.