PermissionError
[Errno 13] Permission denied — the path exists, but the OS will not let this process do that to it. One class for two errnos: EACCES and EPERM.
Demo
import errno raise OSError(errno.EACCES, 'Permission denied', '/etc/shadow')
Trigger raises OSError, yet the traceback says PermissionError — the constructor picked the subclass from errno.EACCES. Errno 13 is the same number on Linux, macOS and Windows. In Raise, a single argument is just the message: errno and strerror stay None, so code that inspects e.errno must not assume it is set.
Constructor
| Name | Type | Required | Description |
|---|---|---|---|
| errno | int | no | errno.EACCES (permission bits, ACLs) or errno.EPERM (operation not allowed at all). Both map to this class. |
| strerror | str | no | 'Permission denied' (EACCES), 'Operation not permitted' (EPERM), 'Access is denied' from Windows API calls. |
| filename | str | bytes | None | no | The path the OS refused. |
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| errno | int | None | errno.EACCES or errno.EPERM when raised by the OS; None for the one-argument form. |
| strerror | str | None | The OS text — "Permission denied", "Operation not permitted", or Windows "Access is denied". |
| filename | str | bytes | None | The refused path. |
| winerror | int | Windows only: 5 (access denied), 32 (file in use by another process), … |
Common patterns
from pathlib import Path try: log = open('/var/log/myapp.log', 'a') except PermissionError: log = open(Path.home() / 'myapp.log', 'a')
try: shutil.copy(src, dst) except PermissionError as e: raise SystemExit(f'cannot write {e.filename}: check ownership or pick another folder')
try: text = open(path).read() except (IsADirectoryError, PermissionError): text = None
Examples
Pitfalls
import errno try: raise OSError(errno.EPERM, 'Operation not permitted', 'app.pid') except OSError as e: r = 'denied' if e.errno == errno.EACCES else 'other error' r
import errno try: raise OSError(errno.EPERM, 'Operation not permitted', 'app.pid') except PermissionError: r = 'denied' r
import errno def read_config(path): # the file exists but is not readable raise PermissionError(errno.EACCES, 'Permission denied', path) try: cfg = read_config('app.ini') except OSError: cfg = {} cfg
import errno def read_config(path): # the file exists but is not readable raise PermissionError(errno.EACCES, 'Permission denied', path) try: cfg = read_config('app.ini') except FileNotFoundError: cfg = {} cfg
When to use
- Falling back to a location the user owns
- Turning a refused path into a clear, actionable message
- Raising it from your own code for an access-control refusal
- Fixing it with sudo — install into a virtualenv or use --user instead
- Checking os.access() first — the result can change before you open; just try
- Treating it as "file missing" — that is FileNotFoundError
Notes
FAQ
Find out which path it is (e.filename) and why: the file belongs to another user or is read-only; you are writing into a system folder (/usr, C:\Program Files); the path is actually a folder (Windows reports that as Permission denied); or, on Windows, another program has the file open. Fix the cause — write to a folder you own, close the other program, change ownership — rather than running Python as administrator.