OSError

Every failed system call ends up here. Since Python 3.3 the constructor looks at errno and hands you the matching subclass — FileNotFoundError, PermissionError and friends.

InheritsBaseException›Exception›OSError
OS exceptionPython 3 (all)Live demo
OSError(errnoerrno — Numeric error code (compare with errno.ENOENT etc.). Decides which subclass the constructor returns.type: int · default: null, strerror[, filename[, winerror[, filename2]]])
Raised by
open(), os.*, shutil.*, pathlib, socket, subprocess
Message
[Errno N] strerror: 'filename'
Quick fix
catch the specific subclass; read e.errno / e.filename
Watch out
IOError, EnvironmentError, socket.error are all OSError

Demo

Live evaluation
Build an OSError from its parts. Watch how filename changes both str(e) and e.args.
Try:
Inputs
errnointerror code
strerrorstrmessage
filenamestr | Noneempty → None
Code
e = OSError(2, 'No such file or directory', 'data.csv')
(e.args, str(e))
Result
((2, 'No such file or directory'), "[Errno 2] No such file or directory: 'data.csv'")

In Raise, a filename makes args shrink to (errno, strerror) and adds : 'name' (repr, with quotes) to the message; with None it is left out and args keeps all three. In Handle, except OSError caught a FileNotFoundError — type(e).__name__ shows the real class the OS error was mapped to.

Constructor

NameTypeRequiredDescription
errnointnoNumeric error code (compare with errno.ENOENT etc.). Decides which subclass the constructor returns.
strerrorstrnoHuman-readable message, normally what the OS reports for that errno.
filenamestr | bytes | NonenoThe path involved. When given (and not None), args is cut down to (errno, strerror).
winerrorint | NonenoWindows only: the native error code. On Windows it overrides errno; elsewhere it is ignored.
filename2str | NonenoSecond path for two-path operations such as os.rename(). Added in 3.4.

Attributes

AttributeTypeMeaning
errnoint | NoneNumeric code. Compare with errno.ENOENT, errno.EACCES … rather than literal numbers — the values differ between Linux, macOS and Windows for many codes.
strerrorstr | NoneThe OS message for that errno (perror() text on POSIX, FormatMessage() on Windows).
filenamestr | bytes | NoneThe path passed to the failing call, exactly as you passed it.
filename2str | NoneSecond path, for os.rename(), os.link() and similar.
winerrorintWindows only — native error code (e.g. 183 for "already exists"). Absent on other platforms.
argstuple(errno, strerror) when a filename was given; otherwise all constructor arguments.

Common patterns

Specific first, OSError last
Handle the cases you understand, then let the base class catch the rest with its details.
try:
    data = open(path).read()
except FileNotFoundError:
    data = ''
except PermissionError:
    raise SystemExit(f'no access to {path}')
except OSError as e:
    raise SystemExit(f'cannot read {path}: {e.strerror}')
Branch on errno with the errno module
For codes without a dedicated subclass (ENOSPC, ENOTEMPTY, EROFS …) compare e.errno with the named constant.
import errno
try:
    os.rmdir(path)
except OSError as e:
    if e.errno != errno.ENOTEMPTY:
        raise
    shutil.rmtree(path)
Raise an OS-style error yourself
Passing errno + strerror + filename gives the same message shape (and subclass) that real I/O errors have.
import errno, os
if not os.path.exists(path):
    raise OSError(errno.ENOENT, 'No such file or directory', path)

Examples

1. The constructor returns a subclass
import errno OSError(errno.ENOENT, 'No such file or directory', 'data.csv')
Returns
FileNotFoundError(2, 'No such file or directory')
2. One argument: errno stays None
e = OSError('disk full') (e.errno, e.strerror, str(e))
Returns
(None, None, 'disk full')
3. filename is not in args
e = OSError(2, 'No such file or directory', 'data.csv') (e.args, e.filename)
Returns
((2, 'No such file or directory'), 'data.csv')
4. IOError and EnvironmentError are aliases
IOError is OSError, EnvironmentError is OSError
Returns
(True, True)
5. A plain OSError: rmdir on a non-empty folder
import errno, os os.mkdir('build') open('build/app.o', 'w').close() try: os.rmdir('build') except OSError as e: r = (type(e).__name__, e.errno == errno.ENOTEMPTY) r
Returns
('OSError', True)
6. The four minor subclasses
import errno [type(OSError(c, 'x')).__name__ for c in (errno.EAGAIN, errno.ECHILD, errno.EINTR, errno.ESRCH)]
Returns
['BlockingIOError', 'ChildProcessError', 'InterruptedError', 'ProcessLookupError']
7. BlockingIOError: third arg is characters_written
import errno e = BlockingIOError(errno.EAGAIN, 'Resource temporarily unavailable', 7) (e.characters_written, e.filename)
Returns
(7, None)
8. Subclassing turns the mapping off
import errno class StorageError(OSError): pass type(StorageError(errno.ENOENT, 'gone')).__name__
Returns
'StorageError'

Pitfalls

1. except OSError before its subclasses
The first matching clause wins. OSError matches every file error, so a subclass clause after it is dead code.
Base class first
try:
    open('missing.txt')
except OSError:
    r = 'generic I/O error'
except FileNotFoundError:
    r = 'create it first'
r
'generic I/O error'
Subclass first
try:
    open('missing.txt')
except FileNotFoundError:
    r = 'create it first'
except OSError:
    r = 'generic I/O error'
r
'create it first'
2. Reading the filename from e.args
With a filename, args holds only (errno, strerror) for backwards compatibility. Use the attribute.
e.args[2]
try:
    open('missing.txt')
except OSError as e:
    name = e.args[2]
name
IndexError: tuple index out of range
e.filename
try:
    open('missing.txt')
except OSError as e:
    name = e.filename
name
'missing.txt'
3. A custom OSError subclass is not remapped
The errno → subclass magic only happens when you construct OSError itself (or an alias). Your subclass stays your subclass, so except FileNotFoundError does not see it.
Subclass + errno
import errno
class AppError(OSError):
    pass
try:
    raise AppError(errno.ENOENT, 'gone')
except FileNotFoundError:
    r = 'missing'
r
AppError: [Errno 2] gone
Raise the real class
import errno
try:
    raise FileNotFoundError(errno.ENOENT, 'gone')
except FileNotFoundError:
    r = 'missing'
r
'missing'

When to use

Use it
  • Catch-all for I/O and system-call failures after the specific subclasses
  • Raising an error that mirrors a real OS failure (errno + strerror + filename)
  • Codes with no subclass of their own: disk full (ENOSPC), directory not empty (ENOTEMPTY), read-only FS (EROFS)
Reach for something else
  • Missing file → catch FileNotFoundError
  • Access denied → PermissionError
  • Bad argument values or types → ValueError / TypeError, not OSError

Notes

CPython impl
Objects/exceptions.c — OSError_new maps errno to a subclass via a lookup table, only when type is OSError itself
PEP 3151
Python 3.3 merged IOError, EnvironmentError, WindowsError, socket.error, select.error and mmap.error into OSError and added the errno subclasses
Catch via
except OSError catches every subclass: FileNotFoundError, PermissionError, ConnectionError, TimeoutError …
Windows
OS calls such as os.mkdir report [WinError N] and a Windows message; open() reports [Errno N] from the C runtime

FAQ

There is none in Python 3. Since 3.3 IOError, EnvironmentError and (on Windows) WindowsError are just other names for OSError: IOError is OSError is True. Old code catching IOError still works; new code should write OSError.

History

3.3
EnvironmentError, IOError, WindowsError, socket.error, select.error and mmap.error merged into OSError; the constructor may return a subclass (PEP 3151).
3.4
filename is the original name passed to the function; filename2 constructor argument and attribute added.