flock Error: 'failed to execute /opt/jobs/sync.sh: No such file or directory' — Cause, Fix, and Troubleshooting Guide
Fix 'flock: failed to execute ...: No such file or directory' and 'flock: 9: Bad file descriptor' — wrong path, missing +x, shebang, and the -c command form.
- #automation
- #troubleshooting
- #flock
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
flock does two things in sequence: it acquires an advisory lock on a file, and then it execs the command you gave it. This error appears after the lock is successfully taken — the locking worked fine, but flock could not run the command:
$ flock -n /var/lock/sync.lock /opt/jobs/sync.sh
flock: failed to execute /opt/jobs/sync.sh: No such file or directory
The message is precise but easy to misread: the lock was acquired, and the failure is purely about executing /opt/jobs/sync.sh. “No such file or directory” here can mean the file genuinely is not there, or that its shebang points at an interpreter that does not exist — the exec syscall reports both the same way. A different but related error appears when you use the file-descriptor form incorrectly:
$ flock -n 9 /opt/jobs/sync.sh
flock: 9: Bad file descriptor
That one means fd 9 was never opened before flock tried to lock it. Both errors come from misusing flock’s two calling conventions, and both are quick to fix once you know which convention you are in.
Symptoms
flock: failed to execute <path>: No such file or directory— lock taken, command not run.flock: failed to execute <path>: Permission denied— the target exists but is not executable.flock: 9: Bad file descriptorwhen using theflock -n <fd>form.- A cron job wrapped in
flocksilently does nothing, and the cron mail contains one of the lines above. - The command runs fine when you type it directly but fails when prefixed with
flock.
Common Root Causes
1. Wrong path or a typo in the command
The path passed to flock does not exist — a typo, a wrong directory, or a relative path evaluated from an unexpected working directory (cron runs with a minimal environment and HOME-based cd assumptions may not hold).
2. The target script is not executable
The file exists but lacks the +x bit. flock tries to exec it directly (not through a shell) and gets Permission denied.
3. Missing or wrong shebang interpreter
The script is executable, but its first line names an interpreter that is not installed (#!/usr/bin/python3 with no python3, or a bad path). The kernel’s exec fails with No such file or directory, referring to the interpreter — not the script.
4. Passing a shell builtin, pipeline, or multiple commands
flock lockfile cmd arg execs a single program directly; there is no shell involved. So flock lock 'a | b', flock lock cd /tmp && x, or flock lock echo $VAR do not behave like shell lines. You must use flock -c '<shell command>' for anything that needs a shell.
5. Working-directory assumptions
The script path or a path inside the script is relative, and flock (or cron) runs it from a different directory than you tested in, so a file “disappears.”
6. Using the fd form without opening the descriptor
flock -n 9 ... locks file descriptor 9, but if you never ran exec 9>/var/lock/sync.lock first, fd 9 is closed — hence flock: 9: Bad file descriptor.
How to Diagnose
Confirm the target actually exists at that exact path and is executable:
ls -l /opt/jobs/sync.sh
ls: cannot access '/opt/jobs/sync.sh': No such file or directory
If it is missing, that is your answer. If it exists, check the permission bits and the interpreter:
ls -l /opt/jobs/sync.sh
head -1 /opt/jobs/sync.sh
file /opt/jobs/sync.sh
-rw-r--r-- 1 myuser myuser 512 Jul 12 08:20 /opt/jobs/sync.sh
#!/usr/bin/env python3
/opt/jobs/sync.sh: Python script, ASCII text executable
No x in -rw-r--r-- explains a Permission denied. Now verify the interpreter the shebang names resolves:
command -v python3 || echo "python3 NOT on PATH"
python3 NOT on PATH
A missing interpreter is why an executable script still fails with “No such file or directory.” Run the command exactly as flock would — directly, not through your interactive shell — to reproduce it in isolation:
/opt/jobs/sync.sh
bash: /opt/jobs/sync.sh: No such file or directory
If that direct invocation fails, flock was never the problem. For the fd form, check whether the descriptor was opened:
# This fails because fd 9 is not open in this shell
flock -n 9 echo hi
flock: 9: Bad file descriptor
Fixes
Correct the path, or make the target executable — whichever the diagnosis showed:
chmod +x /opt/jobs/sync.sh
ls -l /opt/jobs/sync.sh
-rwxr-xr-x 1 myuser myuser 512 Jul 12 08:20 /opt/jobs/sync.sh
Fix a broken shebang so it names an interpreter that exists, and install that interpreter if needed:
sed -i '1s|.*|#!/usr/bin/env python3|' /opt/jobs/sync.sh
command -v python3 # must resolve
For anything that needs a shell — pipelines, redirects, variable expansion, or multiple commands — use the -c form so flock invokes a shell:
# Wrong: flock execs 'psql' directly and treats the rest as its args
flock -n /var/lock/sync.lock psql -c 'VACUUM' | tee vac.log
# Right: give flock one shell command to run
flock -n /var/lock/sync.lock -c 'psql -c "VACUUM" | tee /opt/jobs/vac.log'
For the file-descriptor form, open the descriptor first with exec, then lock it — the idiomatic single-instance pattern:
#!/usr/bin/env bash
set -euo pipefail
exec 9>/var/lock/sync.lock
if ! flock -n 9; then
echo "$(date -Is) another instance holds the lock; skipping" >&2
exit 0
fi
# critical section runs only when the lock is held
/opt/jobs/sync-impl.sh
In cron, use absolute paths for both the lock file and the command, since cron’s working directory and PATH are minimal:
*/5 * * * * /usr/bin/flock -n /var/lock/sync.lock /opt/jobs/sync.sh >> /var/log/sync.log 2>&1
Verify the fix by running it once by hand and confirming the command actually executes under the lock:
flock -n /var/lock/sync.lock /opt/jobs/sync.sh; echo "exit=$?"
sync complete: 214 records
exit=0
What to Watch Out For
- The lock is always acquired before the exec fails, so a broken command still holds and releases the lock momentarily — do not assume a failed
flockmeans the lock was never taken. flock cmdexecs directly with no shell, so$VAR,|,>,&&, and globs are not interpreted. Reach forflock -c '...'the moment you need any of them.flock -n(non-blocking) exits 1 when the lock is held. Distinguish that expected “already running” exit from a real execution failure by checking the message, not just the exit code.- Cron’s environment is not your login shell. A script that runs by hand can fail under cron purely because of a different
PATH,HOME, or working directory — use absolute paths everywhere. - With
exec 9>lockfile, the lock is released when the descriptor closes, which happens automatically when the script exits. Do notrmthe lock file to “release” it — that breaks the advisory-lock model and can let two runs proceed.
Related Guides
- Automation Error: Overlapping Cron Runs Cause a Race Condition
- Scheduled Job Orchestration at Scale
- systemd Timer Failed with Exit Code
Fixed it? Get 500 Automation & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.