Skip to content
Get started

Exit code 126: command found but not executable

Exit code 126 means the shell found the command but could not execute it — almost always because the file is not executable (missing chmod +x) or the interpreter line is wrong. Unlike exit 127 (command not found), the file exists; it just cannot be run. Add the execute bit (chmod +x /path/to/script) and check that the shebang line names a real interpreter.

What is the difference between exit 126 and exit 127?

Both are shell errors that fire before the program even starts, but at different stages of lookup. Exit 127 means the shell searched PATH and found nothing. Exit 126 means the shell found the file but then could not execute it — either the execute bit is missing, the file is a directory, or the specified interpreter in the shebang line does not exist.

The shell reports a message like `bash: /path/to/script.sh: Permission denied` or `bash: /path/to/script.py: /usr/bin/python3: bad interpreter: No such file or directory`. That line in the job's output is the diagnosis.

How do I check for an exit 126?

# Check permissions — the execute bit must be set for the user cron runs as:
ls -l /path/to/script.sh
# -rw-r--r-- 1 user user 1234 Jul  5 00:00 script.sh  ← no x bits = exit 126

# Fix: add the execute bit
chmod +x /path/to/script.sh

# Verify the shebang interpreter exists:
head -1 /path/to/script.sh          # e.g. #!/usr/bin/env python3
which python3                        # confirm the interpreter is on PATH

How do I fix exit 126 in a cron job?

  • Add the execute bit: run chmod +x /path/to/script. In version-controlled repos, set the bit in git (git update-index --chmod=+x script.sh) so deployments don't reset it.
  • Check the shebang line: the first line (#!/usr/bin/env bash, #!/usr/bin/python3, etc.) must name a real interpreter. Run which bash or which python3 to confirm the interpreter path is correct on the cron server.
  • Check ownership: if cron runs the job as a different user (e.g., the system cron daemon runs as root, but the script is owned by a user without world-execute), ensure the execute bit is set for the user cron uses.
  • Don't pass a directory: passing a directory path where a script is expected also produces exit 126. Confirm the path is a file, not a folder.

How do I get alerted when a cron job exits 126?

Like exit 127, a 126 means the job never did any real work. Ping only on success; cron running a non-executable script withholds the ping:

#!/usr/bin/env bash
set -euo pipefail

/path/to/script.sh
curl -fsS -m 10 --retry 3 "https://ping.cronshield.com/<your-check-id>"
On paid tiers, CronShield reads the failing run's output and surfaces the 'Permission denied' or 'bad interpreter' line from the shell directly in the alert. PING_URL is a placeholder for the endpoint you get on a monitor.

Catch this failure automatically

The free tier gives you a heartbeat endpoint and an email alert when an expected ping doesn't arrive. Paid tiers add the log-aware diagnosis — the last log line and a likely cause in the alert. The heartbeat receiver ships in an upcoming release; see the plans to learn what each tier adds.

Frequently asked questions

What is the difference between exit 126 and exit 127?
Exit 127 means the shell searched its PATH and found no file with that name. Exit 126 means the shell found the file but could not execute it — the execute bit is missing, the shebang interpreter is wrong, or it is a directory. Both happen before the program runs any real code.
Can a cron job run a script without the execute permission?
Not directly. If the execute bit is missing, the shell returns exit 126 immediately. The workaround is to invoke the interpreter explicitly (bash /path/to/script.sh instead of /path/to/script.sh), but the correct fix is chmod +x so the file can be executed normally.