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.
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
| Name | Type | Required | Description |
|---|---|---|---|
| errno | int | no | Numeric error code (compare with errno.ENOENT etc.). Decides which subclass the constructor returns. |
| strerror | str | no | Human-readable message, normally what the OS reports for that errno. |
| filename | str | bytes | None | no | The path involved. When given (and not None), args is cut down to (errno, strerror). |
| winerror | int | None | no | Windows only: the native error code. On Windows it overrides errno; elsewhere it is ignored. |
| filename2 | str | None | no | Second path for two-path operations such as os.rename(). Added in 3.4. |
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| errno | int | None | Numeric code. Compare with errno.ENOENT, errno.EACCES … rather than literal numbers — the values differ between Linux, macOS and Windows for many codes. |
| strerror | str | None | The OS message for that errno (perror() text on POSIX, FormatMessage() on Windows). |
| filename | str | bytes | None | The path passed to the failing call, exactly as you passed it. |
| filename2 | str | None | Second path, for os.rename(), os.link() and similar. |
| winerror | int | Windows only — native error code (e.g. 183 for "already exists"). Absent on other platforms. |
| args | tuple | (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.