IsADirectoryError

[Errno 21] Is a directory — you handed a folder to something that wants a file. On Windows the same mistake usually surfaces as PermissionError instead.

InheritsBaseException›Exception›OSError›IsADirectoryError
OS exceptionPython 3.3+Live demo
IsADirectoryError(errnoerrno — errno.EISDIR (21 on Linux, macOS and Windows).type: int · default: null, strerror[, filename])
Raised by
open(folder), os.remove(folder) on Linux, os.rename(file, folder)
Message
[Errno 21] Is a directory: 'path'
Quick fix
filter with Path.is_file(); remove folders with shutil.rmtree
Watch out
Windows raises PermissionError for open(folder)

Demo

Live evaluation
What Linux and macOS raise for open() on a folder: OSError with errno EISDIR — the constructor turns it into IsADirectoryError.
Try:
Inputs
pathstr | Noneempty → None
Code
import errno
raise OSError(errno.EISDIR, 'Is a directory', 'logs')
Uncaught exception
IsADirectoryError: [Errno 21] Is a directory: 'logs'

The Trigger snippet raises OSError but the traceback names IsADirectoryError: errno.EISDIR selected the subclass. That is also why the demo builds the error explicitly — a real open() on a folder only produces this on Linux/macOS; Windows reports PermissionError: [Errno 13] for the same call.

Constructor

NameTypeRequiredDescription
errnointnoerrno.EISDIR (21 on Linux, macOS and Windows).
strerrorstrno'Is a directory'.
filenamestr | bytes | NonenoThe directory path that was used as a file.

Attributes

AttributeTypeMeaning
errnoint | Noneerrno.EISDIR when raised by the OS; None for the one-argument form.
strerrorstr | None'Is a directory'.
filenamestr | bytes | NoneThe folder that was passed where a file was expected.

Common patterns

Read only the files in a folder
listdir()/iterdir() return folders too. Filter before opening.
from pathlib import Path
for p in Path('data').iterdir():
    if p.is_file():
        process(p.read_text())
Delete a file or a folder
os.remove is for files; folders need os.rmdir (empty) or shutil.rmtree (with contents).
import shutil
from pathlib import Path
p = Path(target)
if p.is_dir():
    shutil.rmtree(p)
else:
    p.unlink(missing_ok=True)
Catch the Linux and the Windows variant
The same mistake is IsADirectoryError on POSIX and PermissionError on Windows.
try:
    text = open(path).read()
except (IsADirectoryError, PermissionError):
    text = None

Examples

1. errno EISDIR maps to it
import errno OSError(errno.EISDIR, 'Is a directory', 'logs')
Returns
IsADirectoryError(21, 'Is a directory')
2. Raised with a path
import errno raise IsADirectoryError(errno.EISDIR, 'Is a directory', 'logs')
Returns
IsADirectoryError: [Errno 21] Is a directory: 'logs'
3. open() on a folder, portably
import os os.mkdir('uploads') try: open('uploads') except (IsADirectoryError, PermissionError) as e: r = f'{e.filename} is a folder' r
Returns
'uploads is a folder'
4. Filter with is_file()
from pathlib import Path Path('data').mkdir() Path('data/a.txt').write_text('A') Path('data/archive').mkdir() sorted(p.name for p in Path('data').iterdir() if p.is_file())
Returns
['a.txt']
5. rglob() yields folders too
from pathlib import Path Path('data/sub').mkdir(parents=True) Path('data/a.txt').write_text('A') Path('data/sub/b.txt').write_text('B') sorted(p.as_posix() for p in Path('data').rglob('*'))
Returns
['data/a.txt', 'data/sub', 'data/sub/b.txt']
6. shutil.rmtree removes a folder
import os, shutil os.mkdir('build') open('build/app.o', 'w').close() shutil.rmtree('build') os.path.exists('build')
Returns
False

Pitfalls

1. Opening every entry of listdir()
os.listdir() returns sub-folder names as well as files. The first folder breaks the loop (IsADirectoryError on Linux/macOS, PermissionError on Windows).
All entries
import os
from pathlib import Path
os.mkdir('data')
Path('data/a.txt').write_text('A')
os.mkdir('data/old')
try:
    texts = [open(f'data/{n}').read() for n in sorted(os.listdir('data'))]
except (IsADirectoryError, PermissionError) as e:
    texts = f'failed on {e.filename}'
texts
'failed on data/old'
Files only
import os
from pathlib import Path
os.mkdir('data')
Path('data/a.txt').write_text('A')
os.mkdir('data/old')
texts = [p.read_text() for p in sorted(Path('data').iterdir()) if p.is_file()]
texts
['A']
2. os.remove() on a folder
os.remove/os.unlink delete files only. On a folder Linux raises IsADirectoryError, macOS and Windows raise PermissionError. Use shutil.rmtree (or os.rmdir for an empty folder).
os.remove
import os
os.mkdir('cache')
try:
    os.remove('cache')
except (IsADirectoryError, PermissionError):
    r = 'not removed'
r
'not removed'
shutil.rmtree
import os, shutil
os.mkdir('cache')
shutil.rmtree('cache')
os.path.exists('cache')
False

When to use

Use it
  • Catching a folder passed where a file path was expected (together with PermissionError for Windows)
  • Raising it from your own API when given a directory instead of a file
Reach for something else
  • Walking a tree → filter with Path.is_file() / os.walk() files lists
  • Deleting folders → shutil.rmtree() or os.rmdir()
  • Relying on it on Windows → the same mistake is PermissionError there

Notes

CPython impl
Objects/exceptions.c — OSError(EISDIR, …) returns IsADirectoryError
Catch via
except OSError, or (IsADirectoryError, PermissionError) for portable code
Platforms
open(folder): Linux/macOS IsADirectoryError, Windows PermissionError. os.remove(folder): Linux IsADirectoryError, macOS/Windows PermissionError

FAQ

The path you gave to open(), os.remove() or similar is a folder, not a file. Common causes: a loop over os.listdir() that also returns sub-folders, a path variable missing its file name (data/ instead of data/file.csv), or a save path that points at an existing folder.

History

3.3
Added, together with the other OSError subclasses (PEP 3151).