Python’s errno module exposes POSIX system error number constants and a mapping to their symbolic names. import errno. Constants: errno.ENOENT (2), errno.EACCES (13), errno.EEXIST (17), errno.EISDIR (21), errno.ENOTDIR (20), errno.ENOTEMPTY (39), errno.EAGAIN / errno.EWOULDBLOCK (11/11 on Linux), errno.EINTR (4), errno.EPIPE (32), errno.ECONNREFUSED (111), errno.ETIMEDOUT (110), errno.EADDRINUSE (98), errno.ENOMEM (12), errno.E2BIG (7), errno.EBUSY (16). Look up by number: errno.errorcode[n] → "ENOENT" string; os.strerror(n) → "No such file or directory". Check an OSError: e.errno == errno.ENOENT — the .errno attribute on OSError subclasses (FileNotFoundError, PermissionError, IsADirectoryError, etc.) holds the error number. Platform notes: not all constants exist on all platforms; use hasattr(errno, "ENOENT") or wrap in try/except AttributeError. Network codes: errno.ECONNRESET (104), errno.ENETUNREACH (101), errno.ENOPROTOOPT (92). Claude Code generates error-specific exception handlers, retry logic, network error classifiers, and portable cross-platform error message formatters.
CLAUDE.md for errno
## errno Stack
- Stdlib: import errno, os
- Check: except OSError as e: if e.errno == errno.ENOENT: ...
- Name: errno.errorcode[e.errno] # "ENOENT"
- Msg: os.strerror(e.errno) # "No such file or directory"
- Common: ENOENT EACCES EEXIST EAGAIN EPIPE ECONNREFUSED ETIMEDOUT
- Note: Python 3 maps errno to named exceptions:
- ENOENT → FileNotFoundError EACCES → PermissionError
errno Error Code Pipeline
# app/errnoutil.py — error checks, retry, classifiers, formatter
from __future__ import annotations
import errno
import os
import socket
import time
from dataclasses import dataclass
from typing import Callable
# ─────────────────────────────────────────────────────────────────────────────
# 1. Error code inspection helpers
# ─────────────────────────────────────────────────────────────────────────────
def errno_name(code: int) -> str:
"""
Return the symbolic name for an errno code ("ENOENT", "EACCES", ...).
Falls back to the decimal string if unknown.
Example:
print(errno_name(2)) # "ENOENT"
print(errno_name(13)) # "EACCES"
"""
return errno.errorcode.get(code, str(code))
def errno_message(code: int) -> str:
"""
Return the human-readable strerror message for an errno code.
Example:
print(errno_message(2)) # "No such file or directory"
"""
return os.strerror(code)
def oserror_info(exc: OSError) -> dict:
"""
Return a structured dict with errno, name, message, and filename from an OSError.
Example:
try:
open("/nonexistent")
except OSError as e:
print(oserror_info(e))
"""
code = exc.errno or 0
return {
"errno": code,
"name": errno_name(code),
"message": errno_message(code),
"filename": exc.filename,
"strerror": exc.strerror or "",
}
# ─────────────────────────────────────────────────────────────────────────────
# 2. Error category classifiers
# ─────────────────────────────────────────────────────────────────────────────
_TRANSIENT_ERRNO = frozenset(filter(None, [
getattr(errno, "EAGAIN", None),
getattr(errno, "EWOULDBLOCK", None),
getattr(errno, "EINTR", None),
getattr(errno, "ETIMEDOUT", None),
getattr(errno, "ENETUNREACH", None),
getattr(errno, "ECONNRESET", None),
getattr(errno, "ECONNABORTED", None),
]))
_PERMISSION_ERRNO = frozenset(filter(None, [
getattr(errno, "EACCES", None),
getattr(errno, "EPERM", None),
]))
_NOT_FOUND_ERRNO = frozenset(filter(None, [
getattr(errno, "ENOENT", None),
getattr(errno, "ENODEV", None),
getattr(errno, "ENXIO", None),
]))
_NETWORK_ERRNO = frozenset(filter(None, [
getattr(errno, "ECONNREFUSED", None),
getattr(errno, "ECONNRESET", None),
getattr(errno, "ETIMEDOUT", None),
getattr(errno, "ENETUNREACH", None),
getattr(errno, "EHOSTUNREACH", None),
getattr(errno, "EPIPE", None),
]))
def is_transient(exc: OSError) -> bool:
"""
Return True if the error is likely transient and worth retrying.
Example:
except OSError as e:
if is_transient(e):
retry()
else:
raise
"""
return exc.errno in _TRANSIENT_ERRNO
def is_permission_error(exc: OSError) -> bool:
"""Return True for EACCES / EPERM."""
return exc.errno in _PERMISSION_ERRNO
def is_not_found(exc: OSError) -> bool:
"""Return True for ENOENT and related not-found codes."""
return exc.errno in _NOT_FOUND_ERRNO
def is_network_error(exc: OSError) -> bool:
"""Return True for connection/network-related errno codes."""
return exc.errno in _NETWORK_ERRNO
def error_category(exc: OSError) -> str:
"""
Classify an OSError into a string category for structured logging.
Example:
except OSError as e:
log.warning("io error", category=error_category(e))
"""
if is_permission_error(exc):
return "permission"
if is_not_found(exc):
return "not_found"
if is_network_error(exc):
return "network"
if is_transient(exc):
return "transient"
return "other"
# ─────────────────────────────────────────────────────────────────────────────
# 3. Retry with errno-aware backoff
# ─────────────────────────────────────────────────────────────────────────────
def retry_on_transient(
fn: Callable,
*args: object,
max_attempts: int = 5,
base_delay: float = 0.1,
backoff: float = 2.0,
**kwargs: object,
) -> object:
"""
Retry fn on transient OSError (EAGAIN, EINTR, ECONNRESET, etc.).
Raises immediately on non-transient errors or after max_attempts.
Example:
result = retry_on_transient(socket.connect, (host, port), max_attempts=3)
"""
last_exc: OSError | None = None
delay = base_delay
for attempt in range(1, max_attempts + 1):
try:
return fn(*args, **kwargs)
except OSError as e:
if not is_transient(e):
raise
last_exc = e
if attempt < max_attempts:
time.sleep(delay)
delay = min(delay * backoff, 30.0)
assert last_exc is not None
raise last_exc
# ─────────────────────────────────────────────────────────────────────────────
# 4. Safe file operations with errno handling
# ─────────────────────────────────────────────────────────────────────────────
def safe_unlink(path: "str | os.PathLike") -> bool:
"""
Remove a file, ignoring ENOENT (already gone). Returns True if deleted.
Example:
safe_unlink("/tmp/lock.pid")
"""
try:
os.unlink(str(path))
return True
except OSError as e:
if e.errno == errno.ENOENT:
return False
raise
def safe_mkdir(path: "str | os.PathLike") -> bool:
"""
Create a directory, ignoring EEXIST. Returns True if newly created.
Example:
safe_mkdir("/data/cache")
"""
try:
os.mkdir(str(path))
return True
except OSError as e:
if e.errno == errno.EEXIST:
return False
raise
def read_if_exists(path: "str | os.PathLike", default: str = "") -> str:
"""
Read a text file, returning default on ENOENT.
Example:
config = read_if_exists("/etc/myapp/config.ini")
"""
try:
with open(str(path)) as f:
return f.read()
except OSError as e:
if e.errno == errno.ENOENT:
return default
raise
def is_port_in_use(port: int, host: str = "127.0.0.1") -> bool:
"""
Return True if a TCP port is already bound (EADDRINUSE).
Example:
if is_port_in_use(8080):
print("port 8080 is busy")
"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
s.bind((host, port))
return False
except OSError as e:
if e.errno == errno.EADDRINUSE:
return True
raise
# ─────────────────────────────────────────────────────────────────────────────
# 5. errno reference table
# ─────────────────────────────────────────────────────────────────────────────
_IMPORTANT_ERRORS = [
"EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO", "E2BIG",
"ENOEXEC", "EBADF", "ECHILD", "EAGAIN", "ENOMEM", "EACCES", "EFAULT",
"EBUSY", "EEXIST", "EXDEV", "ENODEV", "ENOTDIR", "EISDIR", "EINVAL",
"ENFILE", "EMFILE", "ENOSPC", "ESPIPE", "EROFS", "EPIPE", "ERANGE",
"EDEADLK", "ENAMETOOLONG", "ENOTEMPTY", "ELOOP", "EWOULDBLOCK",
"ENOMSG", "ENOLINK", "EPROTO", "EMULTIHOP", "EBADMSG", "ENOTCONN",
"ECONNREFUSED", "ECONNRESET", "ECONNABORTED", "ETIMEDOUT",
"EADDRINUSE", "EADDRNOTAVAIL", "ENETUNREACH", "EHOSTUNREACH", "EPIPE",
]
def errno_table() -> list[dict]:
"""
Return a list of dicts for common errno constants available on this platform.
Example:
for row in errno_table():
print(f" {row['code']:3d} {row['name']:20s} {row['message']}")
"""
result = []
for name in _IMPORTANT_ERRORS:
val = getattr(errno, name, None)
if val is not None:
result.append({
"code": val,
"name": name,
"message": os.strerror(val),
})
return result
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
import tempfile
from pathlib import Path
print("=== errno demo ===")
# ── basic lookups ─────────────────────────────────────────────────────────
print("\n--- errno name + message ---")
for code in [errno.ENOENT, errno.EACCES, errno.EEXIST,
errno.EAGAIN, errno.EPIPE, errno.ECONNREFUSED]:
print(f" {code:3d} {errno_name(code):20s} {errno_message(code)}")
# ── oserror_info ──────────────────────────────────────────────────────────
print("\n--- oserror_info ---")
try:
open("/no/such/path/exists")
except OSError as e:
info = oserror_info(e)
print(f" {info}")
# ── error_category ────────────────────────────────────────────────────────
print("\n--- error_category ---")
for code, desc in [
(errno.ENOENT, "missing file"),
(errno.EACCES, "permission denied"),
(errno.EAGAIN, "would block"),
(errno.ECONNREFUSED, "connection refused"),
]:
exc = OSError(code, os.strerror(code))
exc.errno = code
print(f" {desc}: category={error_category(exc)}")
# ── safe_unlink / safe_mkdir ──────────────────────────────────────────────
print("\n--- safe_unlink / safe_mkdir ---")
with tempfile.TemporaryDirectory() as td:
p = Path(td) / "lock.pid"
print(f" safe_unlink (missing): {safe_unlink(p)}")
p.write_text("42")
print(f" safe_unlink (exists): {safe_unlink(p)}")
sub = Path(td) / "cache"
print(f" safe_mkdir (new): {safe_mkdir(sub)}")
print(f" safe_mkdir (exists): {safe_mkdir(sub)}")
# ── is_port_in_use ────────────────────────────────────────────────────────
print("\n--- is_port_in_use ---")
with socket.socket() as s:
s.bind(("127.0.0.1", 0))
port = s.getsockname()[1]
print(f" port {port} (bound): {is_port_in_use(port)}")
print(f" port {port} (free): {is_port_in_use(port)}")
# ── errno_table excerpt ───────────────────────────────────────────────────
print("\n--- errno_table (first 8) ---")
for row in errno_table()[:8]:
print(f" {row['code']:3d} {row['name']:20s} {row['message']}")
print("\n=== done ===")
For the os.strerror alternative — os.strerror(code) returns the platform error message for a numeric code; it is the same call made internally by OSError.__str__() — use os.strerror when you have a raw integer errno and need the message without constructing an exception; use errno.errorcode for the symbolic name. For Python 3 named exceptions — FileNotFoundError (ENOENT), PermissionError (EACCES/EPERM), IsADirectoryError (EISDIR), NotADirectoryError (ENOTDIR), FileExistsError (EEXIST), BlockingIOError (EAGAIN/EWOULDBLOCK), TimeoutError (ETIMEDOUT), ConnectionRefusedError (ECONNREFUSED), BrokenPipeError (EPIPE) — these are subclasses of OSError that match except FileNotFoundError without checking .errno; use named exceptions for single-error catches in idiomatic Python 3 code; use errno constants when you need to handle multiple codes in one except OSError block or when writing cross-version code. The Claude Skills 360 bundle includes errno skill sets covering errno_name()/errno_message()/oserror_info() code inspection, is_transient()/is_permission_error()/is_not_found()/is_network_error()/error_category() classifiers, retry_on_transient() errno-aware backoff retry, safe_unlink()/safe_mkdir()/read_if_exists()/is_port_in_use() safe wrappers, and errno_table() reference listing. Start with the free tier to try error code patterns and errno pipeline code generation.