Checking paths before reading or writing is a routine part of file I/O. For new code, the pathlib module is usually the clearest choice: it treats paths as objects and pairs well with open() and other APIs. The older os.path functions remain common in existing projects—build string paths with os.path.join instead of concatenating slashes by hand. This page orders ideas as pathlib first, then os.path, then file vs directory, and finally opening with try / except so existence checks line up with how the interpreter and OS actually behave. For writing after a check, see Python write to file.
Tested on: Python 3.13.3; kernel 6.14.0-37-generic.
Check if a file exists using pathlib
The documentation describes pathlib as providing object-oriented filesystem paths. A Path wraps a string path and exposes exists(), is_file(), is_dir(), and related checks without splitting string logic across the codebase.
When the question is specifically “is there a regular file here?”, Path.is_file() is the direct test (it follows symlinks to the target; a symlink pointing at a file counts as a file).
from pathlib import Path
path = Path("data.txt")
if path.is_file():
print("File exists")
else:
print("File does not exist")If data.txt is missing or the path is a directory, is_file() is False. The snippet assumes the process current working directory contains data.txt; relative paths depend on where the script is launched from.
Check if a path exists using Path.exists()
Path.exists() returns True if anything exists at that path that stat() can resolve: a regular file, a directory, or a symlink whose target is reachable. It is False for a missing path and typically False for a broken symlink (nothing to stat at the leaf).
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as d:
base = Path(d)
(base / "any.txt").write_text("x", encoding="utf-8")
(base / "folder").mkdir()
print((base / "any.txt").exists()) # True: file
print((base / "folder").exists()) # True: directory
print((base / "missing").exists()) # FalseUse exists() when the program only needs to know “is there something at this path?” before choosing the next step (for example, creating a parent folder vs opening a file).
Check if the path is a file using Path.is_file()
Path.is_file() is True only when the path refers to an existing regular file (or a symlink whose final target is a regular file). A directory or a broken symlink yields False.
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as d:
base = Path(d)
f = base / "demo.txt"
f.write_text("hello", encoding="utf-8")
(base / "subdir").mkdir()
print(f.is_file()) # True
print((base / "subdir").is_file()) # False: it is a directoryCheck if a file exists using os.path.exists()
os.path.exists(path) mirrors Path.exists(): True for an existing file, directory, or working symlink. It does not distinguish object types.
import os
import os.path
import tempfile
with tempfile.NamedTemporaryFile(delete=False) as tmp:
name = tmp.name
try:
print(os.path.exists(name)) # True while the file is present
finally:
os.unlink(name)
print(os.path.exists(name)) # False after removalCheck if a file exists using os.path.isfile()
os.path.isfile(path) is True only for an existing regular file (symlinks to files count). Directories return False.
import os
import os.path
import tempfile
with tempfile.NamedTemporaryFile(delete=False) as tmp:
name = tmp.name
print(os.path.isfile(name)) # True
os.unlink(name)
print(os.path.isfile(name)) # Falseexists() vs is_file() (pathlib and os.path)
| Question | pathlib |
os.path |
|---|---|---|
| Does anything exist here (file, directory, or a symlink whose target stat succeeds)? | Path(p).exists() |
os.path.exists(p) |
| Is this path a regular file (or a symlink whose final target is a regular file)? | Path(p).is_file() |
os.path.isfile(p) |
Use is_file() / isfile() before reading file bytes so a directory at the same path does not pass the check. Use exists() when the logic only needs “nothing vs something” (for example skipping mkdir when the path is already taken). Mixing the two often shows up as bugs where a directory name is passed into APIs that expect a file path.
Check if a file exists before reading
An explicit check keeps error messages and control flow in application code instead of the default FileNotFoundError traceback—useful for CLI messages or skipping optional inputs.
from pathlib import Path
path = Path("config.json")
if not path.is_file():
raise SystemExit("config.json is required next to this script")
text = path.read_text(encoding="utf-8")Remember the time gap between the check and read_text: another process can remove the file in between, so the read can still fail.
Better option: handle FileNotFoundError while opening
Opening (or using Path.read_text) inside try / except FileNotFoundError handles both “never existed” and “disappeared after a check” without a race between two OS calls. That pattern is the usual recommendation for robust readers. For causes, errno 2 text, and path debugging, see FileNotFoundError in Python.
try:
with open("data.txt", "r", encoding="utf-8") as file:
content = file.read()
except FileNotFoundError:
print("File does not exist")The same idea applies to Path.read_text() and to catching OSError when both missing files and permission errors must be grouped.
Check if a directory exists
Directories satisfy exists() but not is_file(). Use Path.is_dir() or os.path.isdir() when the path must be a folder (for example before mkdir parents or walking logs).
from pathlib import Path
import tempfile
with tempfile.TemporaryDirectory() as d:
path = Path(d)
print(path.is_dir()) # True
print(path.is_file()) # FalseCheck if a symlink exists
Path.is_symlink() and os.path.islink() test whether the path itself is a symlink entry, without requiring the target to exist. A broken link can still be is_symlink() True while exists() is False. For day-to-day “is this a normal file I can open?”, prefer is_file(); reserve symlink-specific checks for packaging, deployment, or security reviews. Linux background on creating links: create symbolic link.
import os
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as d:
base = Path(d)
target = base / "target.txt"
target.write_text("x", encoding="utf-8")
link = base / "link.txt"
link.symlink_to(target)
print(link.is_symlink(), link.is_file()) # True True: link points to a file
target.unlink()
print(link.is_symlink(), link.exists(), link.is_file()) # e.g. True False False after target removed (Linux)On Linux, deleting the target file usually leaves the symlink entry in place: is_symlink() stays true while exists() and is_file() become false until the link itself is deleted. Other platforms can differ slightly; treat “symlink present” and “open will succeed” as separate checks.
Common mistakes when checking file existence
- Using
exists()(oros.path.exists()) when the code must only accept a regular file; a directory at the same path still makesexists()true. - Assuming a relative path is relative to the script file—it is relative to the current working directory, which differs in IDEs, cron, and systemd units compared to an interactive shell.
- Relying on “check then open” without handling
FileNotFoundError, so a race or stale check still crashes the program. - Treating a broken symlink as “file exists” because older logic only called
islink(); combineis_file()orexists()with the behavior the read API needs. - Case sensitivity on Linux:
Path("Data.txt")andPath("data.txt")are different paths; on typical Windows volumes the same spelling may collide depending on filesystem rules.
Python file exists quick reference table
| Need | Use |
|---|---|
| Path is a regular file | Path("file.txt").is_file() |
| Path exists as file, directory, or resolvable link | Path("file.txt").exists() |
| Classic API: regular file | os.path.isfile(path) |
| Classic API: any existing path | os.path.exists(path) |
| Directory | Path("folder").is_dir() |
| Safer read | try / except FileNotFoundError around open or read_text |
| Symlink entry (path is a link) | Path(path).is_symlink() or os.path.islink(path) |
Summary
Prefer pathlib.Path for new Python: use is_file() when only a regular file counts, and exists() when any present path is enough. On legacy code, os.path.isfile and os.path.exists follow the same split. Directories are handled with is_dir() or os.path.isdir. Because the filesystem can change between a check and an open, pairing checks with try / except FileNotFoundError (or reading directly and catching the error) is more reliable than a bare check alone; see try / except in Python for broader patterns.
References
- pathlib — Object-oriented filesystem paths (Python standard library)
- os.path — Common pathname manipulations (Python standard library)

