ENOENT

chmod: cannot access 'file': No such file or directory Fix

You're running chmod on a file that doesn't exist at that exact path — usually a typo, a broken symlink, or a directory you forgot to cd into. Here's how to find the real path fast.

You typed chmod +x deploy.sh, hit enter, and got back chmod: cannot access 'deploy.sh': No such file or directory. The file is right there in your editor. What's actually happening here is that chmod never looks at your editor's buffer — it asks the kernel to resolve the path you gave it, and the kernel says that path doesn't exist. Every cause below is just a different reason the kernel disagrees with your brain about where the file lives.

Cause 1: You're in the wrong working directory (by far the most common)

Open a fresh terminal, chmod a relative path, boom. Your shell's current directory is whatever pwd prints, not where you think you are. If you opened a new tab in tmux, or ssh'd into a box after a cd session, you're back in $HOME.

Check where you actually are:

pwd
ls -la deploy.sh

If ls also says no such file, you're not where you thought. Either cd to the right place or pass the full path. I can't count how many times someone has "fixed" this by copying the file into their home directory, which just leaves a stray duplicate that later runs the wrong version of the script. Don't do that — cd instead.

If you're in a script, that same bug bites harder. Scripts inherit the caller's working directory, not the script's location. So this:

#!/bin/bash
chmod +x helper.sh

fails the moment someone runs the script from /tmp. The robust fix is to anchor on the script's own directory:

#!/bin/bash
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
chmod +x "$SCRIPT_DIR/helper.sh"

The reason that one-liner works is BASH_SOURCE[0] gives you the path the script was invoked with, dirname strips the filename, and the cd && pwd resolves it to an absolute path even when the script was called through a symlink or a relative path.

Cause 2: A broken symlink, or chmod refusing to follow one

Symlinks trip people up in two directions. A symlink pointing at a target that no longer exists will make chmod return No such file or directory — because chmod resolves the link first, and the target isn't there. You can spot it with ls -l:

$ ls -l /usr/local/bin/node
lrwxrwxrwx 1 root root 26 Jan 12 09:14 /usr/local/bin/node -> /opt/node-v20/bin/node
$ ls /opt/node-v20
ls: cannot access '/opt/node-v20': No such file or directory

Someone uninstalled node 20 and left the symlink behind. chmod +x /usr/local/bin/node fails even though ls -l shows the link. The fix is either repoint the link (ln -sfn /opt/node-v22/bin/node /usr/local/bin/node) or delete it.

The opposite case: your file is a symlink, the target exists, and chmod silently changes permissions on the target, not the link (that's POSIX behavior — chmod follows symlinks by default). If you actually wanted to change the link itself, use chmod -h, though on most filesystems link permissions are ignored anyway. And if you're scripting something that should skip broken links, GNU find with ! -xtype l excludes them cleanly:

find . -maxdepth 1 -type f ! -xtype l -exec chmod 644 {} +

Cause 3: Shell expansion ate the filename (spaces, wildcards, quotes)

Filenames with spaces, globs that don't match, and missing quotes all collapse into this error. If there's no file matching *.log in the current directory, bash (without nullglob set) passes the literal string *.log to chmod, which then reports:

chmod: cannot access '*.log': No such file or directory

Notice the quotes around *.log in the error — that's the giveaway. chmod is looking for a file literally named *.log. The files you expected didn't match because you're in the wrong directory (see cause 1) or nothing matches your pattern.

Filenames with spaces are the other classic. This fails:

chmod 644 my report.txt

chmod sees two arguments — my and report.txt — and neither exists. Quote it:

chmod 644 "my report.txt"

If you're looping over filenames, always quote the variable and use -- so a filename starting with a dash doesn't get parsed as a flag:

for f in *.txt; do
    chmod 644 -- "$f"
done

Better still, make the loop tolerate zero matches:

shopt -s nullglob
for f in *.txt; do chmod 644 -- "$f"; done

Without nullglob, an empty directory makes the loop run once with the literal string *.txt and chmod fails. With it, the loop body never executes. That one line has saved me more support tickets than any other bash option.

Quick reference

SymptomLikely causeFix
Error appears in a new shell/tabWrong working directorypwd, then cd or use absolute path
Script works interactively, fails when run elsewhereScript assumes caller's cwdAnchor paths with BASH_SOURCE / script dir
Error quotes the filename literally with a *Glob didn't match anythingCheck directory, or enable shopt -s nullglob
File has spaces in its nameWord splittingQuote the path: chmod 644 "my file"
ls -l shows the file but chmod can't find itBroken symlinkRepoint with ln -sfn or delete the link
Filename starts with -Parsed as a flagUse -- before the filename
Case mismatchLinux is case-sensitiveREADME ≠ readme — check with ls

One last thing worth internalizing: this error is ENOENT surfacing from the kernel's stat() call. chmod never got as far as changing permissions — it couldn't find the inode. So don't chase permission problems, SELinux, or filesystem mounts. Find the path the kernel can actually see, and chmod will start working.

Related Errors in Linux & Unix
SSH Authentication Loop: SSH Server Rejects Your Key Then Re-prompts Your Hostname Keeps Reverting After Reboot? Here's the Fix Linux OOM Killer: When Your App Gets Murdered by the Kernel Wine App Crashes Right After Starting? Fix It Here

Was this solution helpful?

EP
Erropedia Team
Tech Support Editors
The Erropedia editorial team researches and documents real-world tech errors from across Windows, Linux, macOS, networking, databases, cloud platforms, and more. Every solution is reviewed for accuracy and updated as software and systems evolve.