FileNotFoundError: [Errno 2] No such file or directory
1 · The lesson
readWhat this error means
The OS tried to open the path you gave it, and there is nothing at that path.FileNotFoundError is how Python reports the C error ENOENT (errno 2), which the system call open(2) returns. It is a subclass of OSError, so generic except OSError will catch it.
When you see it
Traceback (most recent call last):
File "load.py", line 3, in <module>
with open("data.txt") as f:
FileNotFoundError: [Errno 2] No such file or directory: 'data.txt'Why it happens
A relative path like"data.txt" is resolved against the current working directory (CWD) of the process, not against the directory where your script lives. If you run python script.py from a different folder, or if your IDE uses a different CWD than your terminal, Python looks for the file in a different folder than you expect. Other causes: symlinks that point to nothing, files deleted between a check and the open, and typos.
How to fix it
Option 1: build an absolute path from the script's location. This is the right answer for files that are part of your project.
from pathlib import Path HERE = Path(__file__).resolve().parent data_path = HERE / "data.txt" with data_path.open() as f: contents = f.read()
Path(__file__).resolve().parent always points to the script's own directory, whatever the CWD is.
Option 2: check where Python is looking.
from pathlib import Path print("CWD:", Path.cwd()) print("Looking for:", Path("data.txt").resolve())
If Path.cwd() is not what you expected, that is the bug.
Option 3: check that the file exists first, when a missing file is a normal case.
from pathlib import Path p = Path("optional-config.toml") if p.exists(): config = p.read_text() else: config = ""
Don't use exists() as a check just before you open the file. This is a TOCTOU (time-of-check to time-of-use) race: the file can disappear between the check and the open. Use try/except FileNotFoundError instead:
try: with open("data.txt") as f: data = f.read() except FileNotFoundError: data = ""
Option 4: create the parent directory before writing.
out = Path("reports/2026/q2/summary.csv") out.parent.mkdir(parents=True, exist_ok=True) out.write_text("...")
setup added so this can run · re-runs 3 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) def Path(*_a, **_kw): print('-> Path() called') return _AutoMock('Path()') # Earlier blocks on this page that this one builds on, re-run quietly first. try: from pathlib import Path except Exception: pass _pm_earlier = [ 'from pathlib import Path\n\nHERE = Path(__file__).resolve().parent\ndata_path = HERE / "data.txt"\n\nwith data_path.open() as f:\n contents = f.read()', 'from pathlib import Path\nprint("CWD:", Path.cwd())\nprint("Looking for:", Path("data.txt").resolve())', 'from pathlib import Path\n\np = Path("optional-config.toml")\nif p.exists():\n config = p.read_text()\nelse:\n config = ""', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
When you'd see this in real code
- A script that worked when run from its own folder fails when scheduled by cron or Task Scheduler (different CWD).
- A web app run by gunicorn under systemd cannot find
templates/because CWD is/. - A test loading a fixture file as
"fixtures/sample.json"works in PyCharm but fails on CI because the CI runner's CWD is the repo root.
Related errors
IsADirectoryError: you passed a directory toopen().NotADirectoryError: you used a file path as if it were a directory.PermissionError: the path exists, but you cannot read it. See error-permissionerror.
See Also
- All Python errors: the full index, by type and by when it happens.
- File I/O: opening, reading and writing files safely.
- pathlib: modern path handling.
- cheat-files: quick reference.
Found this useful? Share it with a friend who’s learning Python.