os.remove errors and deleting a file without an error (tested)

os.remove() raises a different OSError subclass for each problem, and the usual ways of ignoring them hide more than you think. We measured each error on Linux and compared the ways to delete a file only if it exists.

1. What os.remove() raises, and when

import os, tempfile

d = tempfile.mkdtemp()
os.mkdir(os.path.join(d, "subdir"))
os.makedirs(os.path.join(d, "full", "x"))
locked = os.path.join(d, "locked"); os.mkdir(locked)
open(os.path.join(locked, "f.txt"), "w").close()
os.chmod(locked, 0o555)                       # directory not writable
ro = os.path.join(d, "readonly.txt"); open(ro, "w").close()
os.chmod(ro, 0o444)                           # file itself read-only

for label, path, fn in [
    ("missing file",              "nope.txt",          os.remove),
    ("a directory",               "subdir",            os.remove),
    ("file in read-only dir",     "locked/f.txt",      os.remove),
    ("read-only file (0o444)",    "readonly.txt",      os.remove),
    ("os.rmdir on non-empty dir", "full",              os.rmdir),
]:
    try:
        fn(os.path.join(d, path))
        print(f"{label:27} removed OK")
    except OSError as e:
        print(f"{label:27} {type(e).__name__}: [Errno {e.errno}] {e.strerror}")
os.chmod(locked, 0o755)

Output:

missing file                FileNotFoundError: [Errno 2] No such file or directory
a directory                 IsADirectoryError: [Errno 21] Is a directory
file in read-only dir       PermissionError: [Errno 13] Permission denied
read-only file (0o444)      removed OK
os.rmdir on non-empty dir   OSError: [Errno 39] Directory not empty

Measured on Linux with Python 3.13.5: a missing file raises FileNotFoundError (errno 2), a directory raises IsADirectoryError (errno 21; use os.rmdir or shutil.rmtree), and a file in a directory you can't write to raises PermissionError (errno 13). A read-only file in a writable directory was deleted without error: on Linux and macOS, deleting a file is a change to its directory, so the file's own permissions don't matter. (Windows refuses to delete read-only files; we couldn't measure that here.) os.rmdir on a non-empty directory raises a plain OSError with errno 39.

2. Removing a file without an error: ignore only "already gone"

import contextlib, os, pathlib, shutil, tempfile

d = tempfile.mkdtemp()
os.mkdir(os.path.join(d, "subdir"))
locked = os.path.join(d, "locked"); os.mkdir(locked)
open(os.path.join(locked, "f.txt"), "w").close()
os.chmod(locked, 0o555)

# 1) ignore only "already gone"
with contextlib.suppress(FileNotFoundError):
    os.remove(os.path.join(d, "nope.txt"))
print("suppress(FileNotFoundError): missing file ignored")

pathlib.Path(d, "nope.txt").unlink(missing_ok=True)
print("Path.unlink(missing_ok=True): missing file ignored")

# 2) missing_ok does NOT hide the other errors
for name in ("subdir", "locked/f.txt"):
    try:
        pathlib.Path(d, name).unlink(missing_ok=True)
    except OSError as e:
        print(f"unlink(missing_ok=True) on {name}: {type(e).__name__}")

# 3) the blanket version hides a file that is still there
try:
    os.remove(os.path.join(locked, "f.txt"))
except OSError:
    pass
print("after 'except OSError: pass', file still exists:",
      os.path.exists(os.path.join(locked, "f.txt")))

# 4) same trap with rmtree(ignore_errors=True)
shutil.rmtree(locked, ignore_errors=True)
print("after rmtree(ignore_errors=True), dir still exists:", os.path.exists(locked))
os.chmod(locked, 0o755)

Output:

suppress(FileNotFoundError): missing file ignored
Path.unlink(missing_ok=True): missing file ignored
unlink(missing_ok=True) on subdir: IsADirectoryError
unlink(missing_ok=True) on locked/f.txt: PermissionError
after 'except OSError: pass', file still exists: True
after rmtree(ignore_errors=True), dir still exists: True

Measured: contextlib.suppress(FileNotFoundError) and Path.unlink(missing_ok=True) (Python 3.8+) both delete-if-present with no error. missing_ok only covers the missing case: the directory and the permission problem still raised. That's what you want. The blanket except OSError: pass hid the PermissionError, and the file was still there afterwards. shutil.rmtree(ignore_errors=True) did the same with a whole directory. If you need "gone, whatever it is", catch FileNotFoundError only and let the rest raise, or log them. Avoid if os.path.exists(p): os.remove(p): another process can delete the file between the check and the remove, and you get FileNotFoundError anyway. For permission problems when deleting trees, see our rmtree PermissionError guide.

Frequently asked questions

How do I remove a file in Python without an error if it doesn't exist?

Path(p).unlink(missing_ok=True) or wrap os.remove(p) in contextlib.suppress(FileNotFoundError). We measured both ignoring a missing file.

Why does os.remove give PermissionError on Linux?

Deleting needs write permission on the directory, not the file. We measured a file in a 0o555 directory failing with errno 13 while a 0o444 file in a writable directory was deleted.

Why does os.remove raise IsADirectoryError?

os.remove only deletes files. We measured errno 21 on a directory; use os.rmdir for empty directories or shutil.rmtree for trees.

Does unlink(missing_ok=True) ignore all errors?

No. We measured it still raising IsADirectoryError and PermissionError; it only ignores FileNotFoundError.

Is except OSError: pass safe around os.remove?

No. We measured it hiding a PermissionError, leaving the file in place with no sign of failure.

Written with AI assistance. Measured on 2026-10-09 on Debian with Python 3.13.5: os.remove on a missing file, a directory, a file in a 0o555 directory and a 0o444 file; os.rmdir on a non-empty directory; suppress, unlink(missing_ok=True), except OSError: pass and rmtree(ignore_errors=True). Every block was run verbatim and its output pasted from that run. Windows behavior was not measured. PlainHeaders is our extension; Python is free.