python/cpython · error · IsADirectoryError

EISDIR

EISDIR

Error message

Is a directory

What it means

Raised by FileIO.__init__ after a successful open when os.fstat() shows the target's st_mode is a directory (S_ISDIR). Opening a directory path for reading/writing succeeds at the OS level on many platforms, so CPython detects it explicitly and raises IsADirectoryError with errno EISDIR and the message from os.strerror(EISDIR) — 'Is a directory'.

Source

Thrown at Lib/_pyio.py:1622

                    raise ValueError('Cannot use closefd=False with file name')
                if opener is None:
                    fd = os.open(file, flags, 0o666)
                else:
                    fd = opener(file, flags)
                    if not isinstance(fd, int):
                        raise TypeError('expected integer from opener')
                    if fd < 0:
                        # bpo-27066: Raise a ValueError for bad value.
                        raise ValueError(f'opener returned {fd}')
                owned_fd = fd
                if not noinherit_flag:
                    os.set_inheritable(fd, False)

            self._closefd = closefd
            self._stat_atopen = os.fstat(fd)
            try:
                if stat.S_ISDIR(self._stat_atopen.st_mode):
                    raise IsADirectoryError(errno.EISDIR,
                                            os.strerror(errno.EISDIR), file)
            except AttributeError:
                # Ignore the AttributeError if stat.S_ISDIR or errno.EISDIR
                # don't exist.
                pass

            if _setmode:
                # don't translate newlines (\r\n <=> \n)
                _setmode(fd, os.O_BINARY)

            self.name = file
            if self._appending:
                # For consistent behaviour, we explicitly seek to the
                # end of file (otherwise, it might be done only on the
                # first write()).
                try:
                    os.lseek(fd, 0, SEEK_END)
                except OSError as e:

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Check the path before opening: os.path.isfile(path) (or Path.is_file()) and report a clear error to the user
  2. Filter directory entries when iterating: if entry.is_dir(): continue
  3. Handle IsADirectoryError explicitly to give a better message than the default

Example fix

# before
f = open(user_path, 'rb')  # user passed a directory -> IsADirectoryError

# after
from pathlib import Path
p = Path(user_path)
if p.is_dir():
    raise ValueError(f'{p} is a directory, expected a file')
f = open(p, 'rb')
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
p = Path(target)
if p.is_dir():
    raise ValueError(f'{p} is a directory, expected a file')
f = open(p, 'rb')

Type guard

def is_file_not_dir(p):
    from pathlib import Path
    p = Path(p)
    return p.exists() and not p.is_dir()

Try / catch

try:
    f = open(path, 'rb')
except IsADirectoryError:
    logger.error('expected a file but got directory: %s', path)
    raise

Prevention

When it happens

Trigger: open('/some/dir', 'rb') or io.FileIO(dirpath, 'r'); globbing or walking a tree and opening every matched path without checking isdir(); user-supplied path pointing at a directory.

Common situations: Treating a directory path as a config/data file; os.scandir/glob results fed into open() unfiltered; scripts where the user passed the folder instead of the file inside it; TOCTOU where a file was replaced by a directory between check and open.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/e330f0e70982ec33. Report an issue: GitHub.