Python OSError errno codes list (tested)
Every OSError carries the operating system's error number in e.errno. We measured the common codes on Python 3.13.5 and Linux, recorded which exception class Python raises for each, and linked our tested fix for each one.
1. The common codes, and the exception class Python raises for each
import errno, os
for n in (1, 2, 9, 13, 17, 20, 21, 22, 28, 32, 98, 104, 110, 111):
try:
raise OSError(n, os.strerror(n))
except OSError as e:
print(f"{n:>3} {errno.errorcode[n]:<13} {type(e).__name__:<23} {e.strerror}")
Output:
1 EPERM PermissionError Operation not permitted
2 ENOENT FileNotFoundError No such file or directory
9 EBADF OSError Bad file descriptor
13 EACCES PermissionError Permission denied
17 EEXIST FileExistsError File exists
20 ENOTDIR NotADirectoryError Not a directory
21 EISDIR IsADirectoryError Is a directory
22 EINVAL OSError Invalid argument
28 ENOSPC OSError No space left on device
32 EPIPE BrokenPipeError Broken pipe
98 EADDRINUSE OSError Address already in use
104 ECONNRESET ConnectionResetError Connection reset by peer
110 ETIMEDOUT TimeoutError Connection timed out
111 ECONNREFUSED ConnectionRefusedError Connection refused
Measured on Python 3.13.5, Linux: constructing OSError(n, ...) with a known code automatically gives you the matching subclass: OSError(2, ...) is a FileNotFoundError. Codes with no dedicated subclass (EBADF 9, EINVAL 22, ENOSPC 28, EADDRINUSE 98) stay plain OSError, and for those you have to check e.errno. Both 1 and 13 map to PermissionError. The numbers are Linux's; other systems use different numbers for some codes, which is why you should compare against the errno.EXXX names, never the numbers.
2. What's on the exception
import errno
try:
open("/nonexistent/config.json")
except OSError as e:
print(type(e).__name__, "| errno:", e.errno, "| strerror:", e.strerror, "| filename:", e.filename)
print(e.errno == errno.ENOENT, isinstance(e, FileNotFoundError), IOError is OSError)
Output:
FileNotFoundError | errno: 2 | strerror: No such file or directory | filename: /nonexistent/config.json
True True True
Measured: e.errno is the number, e.strerror the OS text, e.filename the path involved (often the fastest clue). IOError is just another name for OSError since Python 3.3 (IOError is OSError is True), so except IOError and except OSError catch exactly the same thing.
3. Handling: subclass first, errno for the rest
import errno, os
def safe_remove(path):
try:
os.remove(path)
except FileNotFoundError:
return "already gone"
except OSError as e:
if e.errno in (errno.EISDIR, errno.EPERM): # Linux / macOS
return f"is a directory ({errno.errorcode[e.errno]})"
raise
return "removed"
os.makedirs("d", exist_ok=True)
open("f.txt", "w").close()
print(safe_remove("f.txt"), "|", safe_remove("f.txt"), "|", safe_remove("d"))
Output:
removed | already gone | is a directory (EISDIR)
Measured: catch the specific subclass when one exists, check e.errno against errno names for the rest, and raise anything you didn't plan for. On Linux, os.remove() on a directory gave EISDIR; we've written the check to also accept EPERM, which other systems use for the same case (not measured here).
Measured fixes for each code
- Errno 2: FileNotFoundError: No such file or directory
- Errno 9: OSError: Bad file descriptor
- Errno 13: PermissionError: Permission denied · shutil.rmtree PermissionError
- Errno 17: FileExistsError: File exists
- Errno 21: IsADirectoryError: Is a directory
- Errno 32: BrokenPipeError: Broken pipe
- Errno 111: ConnectionRefusedError: Connection refused
Frequently asked questions
What are Python OSError error codes?
e.errno holds the operating system's error number, e.g. 2 (ENOENT), 13 (EACCES), 17 (EEXIST). We measured 14 common codes on Linux and the exception class Python raises for each.
How do I get the errno from an OSError?
Use e.errno (number), e.strerror (text) and e.filename (path). Compare e.errno with names from the errno module, e.g. errno.ENOENT, rather than with numbers.
What is the difference between OSError and IOError?
There is none since Python 3.3: IOError is an alias. We measured IOError is OSError returning True.
Which errno values have their own exception class?
We measured FileNotFoundError (2), PermissionError (1, 13), FileExistsError (17), NotADirectoryError (20), IsADirectoryError (21), BrokenPipeError (32), ConnectionResetError (104), TimeoutError (110), ConnectionRefusedError (111). EBADF, EINVAL, ENOSPC and EADDRINUSE stay plain OSError.
Does OSError(2, ...) create a FileNotFoundError?
Yes. We measured OSError with a known errno automatically constructing the matching subclass.
Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5 on Linux: 14 errno values mapped to their exception subclasses, the errno/strerror/filename attributes, IOError vs OSError, and a remove() handler that checks subclasses then errno. Every block was run verbatim and its output pasted from that run. PlainHeaders is our extension; everything else here is built-in Python.