OSError: [Errno 39] Directory not empty in Python (tested)
OSError: [Errno 39] Directory not empty (ENOTEMPTY) comes from os.rmdir, Path.rmdir or os.removedirs on a directory that still has contents, and from os.rename onto an existing non-empty directory. Use shutil.rmtree to delete a tree, or copytree(..., dirs_exist_ok=True) to merge.
1. The error
import os
os.makedirs("out/logs", exist_ok=True)
open("out/logs/a.txt", "w").close()
os.rmdir("out/logs")
Output:
Traceback (most recent call last):
File "/tmp/dn/b1.py", line 4, in <module>
os.rmdir("out/logs")
~~~~~~~~^^^^^^^^^^^^
OSError: [Errno 39] Directory not empty: 'out/logs'
os.rmdir() only removes empty directories. Anything inside (files, subdirectories, hidden files such as .DS_Store) makes it fail with ENOTEMPTY.
2. pathlib.Path.rmdir and os.removedirs behave the same
import os, pathlib
os.makedirs("out2/sub", exist_ok=True)
open("out2/sub/a.txt", "w").close()
for f in (lambda: pathlib.Path("out2/sub").rmdir(), lambda: os.removedirs("out2/sub")):
try:
f()
except OSError as e:
print(type(e).__name__, e.errno, e)
Output:
OSError 39 [Errno 39] Directory not empty: 'out2/sub'
OSError 39 [Errno 39] Directory not empty: 'out2/sub'
Neither deletes contents. os.removedirs only prunes empty parents after removing an empty leaf.
3. os.rename onto an existing directory
import os
os.makedirs("new/x", exist_ok=True); os.makedirs("old/y", exist_ok=True)
try:
os.rename("new", "old") # target dir exists and is not empty
except OSError as e:
print(type(e).__name__, e)
os.makedirs("empty", exist_ok=True)
os.rename("new", "empty") # an EMPTY target directory is replaced
print("renamed onto empty dir:", os.listdir("empty"))
Output:
OSError [Errno 39] Directory not empty: 'new' -> 'old'
renamed onto empty dir: ['x']
On Linux, renaming a directory onto an existing non-empty directory raises the same error, while an empty target is silently replaced. This shows up in deploy scripts that do os.rename("build_new", "build") on the second run.
4. Fix: shutil.rmtree, or merge with copytree
import os, shutil
os.makedirs("out3/logs/deep", exist_ok=True)
open("out3/logs/deep/a.txt", "w").close()
shutil.rmtree("out3")
print("exists after rmtree:", os.path.exists("out3"))
os.makedirs("src/x", exist_ok=True); os.makedirs("dst/old", exist_ok=True)
shutil.copytree("src", "dst", dirs_exist_ok=True) # merge instead of rename
print("dst:", sorted(os.listdir("dst")))
Output:
exists after rmtree: False
dst: ['old', 'x']
shutil.rmtree deletes the whole tree. To merge into an existing directory instead of replacing it, use shutil.copytree(src, dst, dirs_exist_ok=True) (Python 3.8+). To replace atomically-ish, rename the old directory away first, rename the new one in, then rmtree the old one.
5. Catch it precisely
import errno, os
os.makedirs("out4/a", exist_ok=True)
try:
os.rmdir("out4")
except OSError as e:
print(errno.errorcode[e.errno], "| ENOTEMPTY == e.errno:", e.errno == errno.ENOTEMPTY)
print("subclass:", type(e).__name__, "| ls:", os.listdir("out4"))
Output:
ENOTEMPTY | ENOTEMPTY == e.errno: True
subclass: OSError | ls: ['a']
It is a plain OSError (no subclass); check e.errno == errno.ENOTEMPTY.
Related: shutil error handling · rmtree PermissionError · [Errno 16] Device or resource busy.
Frequently asked questions
How do I delete a non-empty directory in Python?
shutil.rmtree(path). os.rmdir and pathlib.Path.rmdir only remove empty directories.
Why does os.rename fail with Directory not empty?
On Linux the target directory exists and has contents. An empty target is replaced; a non-empty one raises Errno 39. Remove or rename the target first.
How do I catch Errno 39?
except OSError as e: if e.errno == errno.ENOTEMPTY. There is no dedicated subclass.
Written with AI assistance. Measured on 2026-10-23 with Python 3.13.5 on Linux (Debian 13, ext4). Each block was run verbatim and the output pasted unedited. PlainTables is our extension; Python is free.