PythonMastery
reference 3 min read · lesson 26 of 60 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 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

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 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.

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__).resolve().parent always points to the script's own directory, whatever the CWD is.

Option 2: check where Python is 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 that the file exists first, when a missing file 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() 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:

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

Option 4: create 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 · 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.
  • 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

Found this useful? Share it with a friend who’s learning Python.