PythonMastery
reference 3 min read · lesson 26 of 45 in Errors

FileNotFoundError: [Errno 2] No such file or directory

1 · The lesson

read

What this error means

The OS tried to open the path you gave it and there is nothing at that path. FileNotFoundError is the Python-level translation of the C ENOENT (errno 2) returned by open(2). It is a subclass of OSError, so generic except OSError will catch it.

When you see it

text
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 has a different CWD than your terminal, the relative lookup goes somewhere unexpected. Symlinks pointing nowhere, files deleted between check and open, and typos round out the list.

How to fix it

Option 1 — use an absolute path anchored to the script. This is the right answer for files that ship with your code.

python
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__).parent always points at the script's own directory, regardless of CWD.

Option 2 — confirm where Python is actually looking.

python
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 existence first when missing is a normal case.

python
from pathlib import Path

p = Path("optional-config.toml")
if p.exists():
    config = p.read_text()
else:
    config = ""

Don't use exists() to guard against an open you immediately do afterwards — that is a TOCTOU race. Just try/except FileNotFoundError:

python
try:
    with open("data.txt") as f:
        data = f.read()
except FileNotFoundError:
    data = ""

Option 4 — make the parent directory before writing.

python
out = Path("reports/2026/q2/summary.csv")
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text("...")
+ setup added so this can run · defines Path
# 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()')

When you'd actually 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.
  • IsADirectoryError — you passed a directory to open().
  • 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