Build the chmod line instead of guessing it
The chmod Permissions Builder converts between 755 and rwxr-xr-x both ways and explains what each bit allows on a file versus a directory. The Bash Exit Code Lookup puts 126 next to 127.
chmod +x fixes the most common Permission denied and nothing else. When it does not work, people reach for sudo, which either fails the same way or succeeds and leaves root-owned files behind for the next run to trip on. The message is the same for five different checks, and the fix for each is different.
Which Operation Was Denied?
Reproduced here on 2026-10-03 (Kali, bash 5.3.15) in a scratch directory. The commands ran from a script, so bash prefixes its own errors with pd.sh: line 1:. First, the one chmod fixes:
Exit 126 is execution. Reads, writes and creates fail with exit 1 from the command that tried:
/etc/demo.conf did not exist. Creating a file is a write to the directory, and /etc is drwxr-xr-x root:root. The same rule explains the surprise that follows:
A read-only file in a writable directory can be deleted. A writable directory can't be told apart from a read-only one by looking at the file.
Why Does chmod +x Not Fix It?
A parent directory without search permission. The x bit on a directory means "may traverse". Without it nothing inside is reachable, whatever the file's own mode is:
locked/backup.sh is -rwxrwxr-x. chmod u+x locked fixes it. namei -l locked/backup.sh prints the mode of every path component, which finds the blocking directory in a long path.
A filesystem mounted noexec. The mount option overrides every execute bit, including root's. Installers that unpack into /tmp hit this on hardened servers. This box's /tmp is not noexec, so the demo uses a throwaway tmpfs mounted noexec inside a user namespace (unshare -rm), which needs no sudo and makes you root inside it:
Root, an executable file, still 126. bash mnt/install.sh works because bash only reads the file; the executable being launched is /bin/bash, which lives on a normal mount. On this box /run is mounted noexec:
Someone else's file. Only the owner (or root) can chmod a file. If ls -l shows another owner, ask them, or fix ownership once with sudo chown you: file rather than running the script under sudo forever.
The Script
Save as why-permission-denied.sh. Give it a path and what you were trying to do (exec, read or write, default exec) and it reports which check blocks you.
Prerequisites
bash 4 or later, coreutils (stat, realpath) and util-linux (findmnt), which every mainstream distro ships. getfacl (package acl) and lsattr (e2fsprogs) are optional; the script skips those checks when they are missing. No root.
How Does Each Check Work?
- The directory walk splits the absolute path and tests
-xon every parent. One directory without search permission is enough to block the file, and the error never names it. - The ownership line says which permission triplet applies to you: owner, group (and whether you are in it), or other. Most "but the group has write" confusion is not being in the group, or being in it only after a fresh login.
getfacl -sprints output only for files with an extended ACL. When one exists, the mode bits are not the whole story.findmnt -no OPTIONS --targetreads the mount options of the filesystem holding the file. It runs before the-xtest because on anoexecmount-xis false even for arwxfile, which would point you atchmodfor no reason.- The shebang check catches the rare interpreter that exists but is not executable. A missing interpreter is exit 127, covered on the command not found page.
lsattrcatches the immutable flag, which turns writes intoOperation not permittedeven for root.
What Does the Script Print?
On the same scratch directory (the user name is shown as travis):
And inside the noexec namespace, where you are root and the file is rwx:
It exits 1 when anything blocks you, so a deploy script can call it before trying: ./why-permission-denied.sh "$TARGET" exec || exit 1.
When Is It "Operation not permitted" Instead?
Operation not permitted is not a mode-bit failure. It means the operation needs privilege, or a flag forbids it whatever the bits say:
Setting the immutable flag needs root, and so does signalling another user's process (PID 1 is root's). Once a file is immutable, writes fail with Operation not permitted even for root, which is why the script checks lsattr on writes.
A port below 1024 is the other common "permission" error, and it is distro-dependent: binding one as a normal user is refused when net.ipv4.ip_unprivileged_port_start is 1024 (the kernel default). This box has it set to 0, so a user can bind port 80 here. Check yours with sysctl net.ipv4.ip_unprivileged_port_start before assuming. List Open Ports on Linux shows what is already listening.
File permissions are worth locking down on purpose, not only fixing when they bite: File Permissions Security audits a tree for world-writable files and loose modes, and The Production Bash Toolkit ships ShellCheck-clean scripts that start from a safe template.
Frequently Asked Questions
What does exit code 126 mean in bash?
126 means the command was found but could not be executed. The usual causes are a missing execute bit, a filesystem mounted noexec, a parent directory without search permission, or a shebang interpreter that exists but is not executable. Compare 127, which means the command was not found at all. Both show up in cron and CI logs, and the number alone tells you which half of the problem to look at.
Why do I get Permission denied after chmod +x?
Either the filesystem is mounted noexec, or a directory above the file is missing its x bit. noexec beats every mode bit, including root's: check with findmnt -no OPTIONS --target ./script.sh and run the script as bash script.sh, or move it somewhere without noexec. A directory without x for you makes everything inside unreachable; namei -l /path/to/script.sh shows the permissions of every component on the way down.
Should I use sudo to fix Permission denied?
Only when the file is meant to be root's, such as /etc/shadow or a system config. For your own scripts, sudo hides the real problem and creates new ones: files the script writes become owned by root, and the next run without sudo fails on those. Fix the mode or the ownership instead, chmod +x script.sh or sudo chown you: file once, and run the script as yourself. sudo also does not get past noexec.
Why can I delete a file I cannot write to?
Because deleting a file changes the directory, not the file. rm needs write and search permission on the directory that holds the name. A file with mode 444 in a directory you own can be removed with rm -f, and a file with mode 666 in a read-only directory cannot. To protect a file from deletion, restrict the directory (or set the sticky bit, which /tmp uses so users can only delete their own files).
What is the difference between Permission denied and Operation not permitted?
Permission denied (EACCES) means a permission check failed: mode bits, an ACL, a directory's search bit or a noexec mount. Operation not permitted (EPERM) means the operation itself is restricted to a privileged process or blocked by a flag, whatever the mode bits say: sending a signal to another user's process, changing a file's owner, or writing to a file with the immutable attribute (chattr +i), which stops even root until the flag is removed.
Part of the bash snippets collection
Related Scripts
- File Permissions Security — find world-writable files and loose modes before an attacker does
- Fix "bash: command not found" — exit 127: the file was never found
- Fix /bin/bash^M: bad interpreter — the other way a file that exists refuses to start
- List Open Ports on Linux — what is listening, and who owns it
- Bash Error Messages, Decoded — this error and eight others, each with the one check that names the cause