PermissionError: [Errno 13] Permission denied: '/app/data/report.csv'
Python · updated 2026-11-04
Raised by open(), os and shutil calls, and socket binds when the operating system refuses the operation for the user the process runs as. The path in the message is the one that was refused; it is often a volume, a log or upload folder, or a cache directory, and it usually works on a laptop where the code runs as you.
Traceback (most recent call last):
File "/app/jobs/export.py", line 27, in write_report
with open(path, "w") as f:
^^^^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: '/app/data/report.csv'Quick fix
Check which user the process runs as (id inside the container) and who owns the path (ls -ld). In Docker, chown the directory to the app user in the Dockerfile, or mount the volume with matching ownership. Write to a directory the app owns (or tempfile) rather than the code directory. If the path is a directory, open a file inside it instead.
- Run id and ls -ld <path> where the process runs (docker exec into the container) to compare the user and the owner.
- Docker: create the directory and chown it to the app user before the USER line, or fix ownership on the mounted volume.
- Write runtime files to a writable location (a data directory you own, /tmp, tempfile) instead of next to the code.
- If the path is a directory (common with os.path.join mistakes), open a file inside it.
- Binding port 80 or 443 as non-root fails the same way on Linux: listen on 8000 and put nginx or the platform router in front.
FROM python:3.12-slim
RUN useradd --create-home app
WORKDIR /app
COPY --chown=app:app . .
# Directories the app writes to, owned by the app user
RUN mkdir -p /app/data /app/logs && chown app:app /app/data /app/logs
USER app
CMD ["gunicorn", "-b", "0.0.0.0:8000", "app:app"]Why it happens
Non-root container on root-owned files
The image switches to a non-root USER, but the directory it writes to was created or copied as root, or a volume is mounted with root ownership.
Writing inside the code directory
Read-only deploys and platforms that mount the app as read-only refuse writes next to the code; caches and uploads need a separate writable path.
A directory where a file was expected
open() on a directory raises IsADirectoryError on Linux and PermissionError on Windows, often from a path join that dropped the filename.
Low port or locked file
Binding a port below 1024 as a normal user on Linux, or opening a file another program has locked on Windows, also ends here.
Other fixes
- Prefer the platform data directory or a mounted volume for anything written at runtime, and keep the code read-only.
- Avoid chmod 777 as a fix: set the right owner instead.
Same error, other wording
- PermissionError: [Errno 13] Permission denied (socket bind on port 80)
- OSError: [Errno 30] Read-only file system: '/app/cache'
- IsADirectoryError: [Errno 21] Is a directory: '/app/data'
Let your coding agent catch the next one
Runheed records this error in production with the stack trace, the breadcrumbs before it and the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:
> Find the PermissionError Errno 13 errors in production, tell me which path and which user, and fix it by writing to a directory the app user owns (Dockerfile chown or a different path), not by loosening permissions.
Related errors
- ModuleNotFoundError: No module named 'x'
- OSError: [Errno 98] Address already in use
- UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
More in Memory, async and the process
- [UnhandledPromiseRejection: This error originated either by throwing inside of an async...Node.js
- FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memoryNode.js
- RangeError: Maximum call stack size exceededNode.js
- RecursionError: maximum recursion depth exceededPython
- RuntimeError: Event loop is closedPython
- RuntimeWarning: coroutine 'fetch_user' was never awaitedPython
- Error: listen EADDRINUSE: address already in use :::3000Node.js
- Error: error:0308010C:digital envelope routines::unsupportedNode.js
- MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error...Node.js
- Error: ENOENT: no such file or directory, open '/app/config.json'Node.js
- Error: EACCES: permission denied, open '/app/data/cache.json'Node.js
- Error: EMFILE: too many open files, open '/app/uploads/0412.jpg'Node.js
References: Python docs: built-in exceptions (PermissionError, OSError), Dockerfile reference: USER