Cron Error: '(CRON) error (grandchild #NNNN failed with exit status 1)' — Cause, Fix, and Troubleshooting Guide
Fix (CRON) error (grandchild #NNNN failed with exit status 1) — why a cron command exits non-zero from minimal PATH, relative paths, or missing env.
- #automation
- #troubleshooting
- #cron
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
(CRON) error (grandchild #NNNN failed with exit status 1) is the cron daemon telling you that the command it launched exited non-zero. Cron forks a child to run each job; that child forks the shell that runs your command (the “grandchild”). When the grandchild returns a non-zero exit status, cron logs it. The number (#21899) is the grandchild’s PID, not an error code — the meaningful part is exit status 1, which is whatever your command returned.
This is not a cron bug. Cron ran the job exactly as scheduled; your command failed. The catch is that the environment cron provides is far more minimal than your interactive shell, so a command that works when you type it can fail under cron.
You will see this in the syslog / journal:
Jul 12 03:15:01 host CRON[21898]: (myuser) CMD (/opt/jobs/backup.sh)
Jul 12 03:15:01 host CRON[21897]: (CRON) error (grandchild #21899 failed with exit status 1)
The first line is the job that was launched; the second is cron reporting that it failed. Note the PID of the grandchild (#21899) differs from the logging process (21897).
Symptoms
journalctl -t CRONor/var/log/syslogshows(CRON) error (grandchild #NNNN failed with exit status 1)right after aCMD (...)line.- The job “works when I run it by hand” but fails on schedule.
- No output anywhere (cron discards stdout/stderr unless an MTA delivers it or you redirect).
- The failure is consistent every scheduled run, or only at boot/early hours before something else is ready.
- Downstream artifacts (backups, exports, reports) are silently missing.
Common Root Causes
1. Minimal PATH under cron
Cron runs with a bare PATH — typically /usr/bin:/bin — not your login PATH. A binary in /usr/local/bin, /snap/bin, or a language version manager (nvm, pyenv, rbenv) will not be found, and the command exits non-zero.
# What cron actually sees; drop this line in a crontab temporarily
* * * * * env > /tmp/cron-env.txt
$ cat /tmp/cron-env.txt
HOME=/home/myuser
LOGNAME=myuser
PATH=/usr/bin:/bin
SHELL=/bin/sh
PWD=/home/myuser
Compare that PATH to your interactive echo $PATH — the difference is usually the cause.
2. Relative paths and wrong working directory
Cron starts the job with PWD set to the user’s home directory. A script that assumes it runs from its own directory (./config.yml, data/input.csv) fails to find its files.
./backup.sh: line 12: config.yml: No such file or directory
3. Missing environment variables
Variables you set in ~/.bashrc, ~/.profile, or a login shell are not present. Cron does not source those files. DATABASE_URL, AWS_PROFILE, NODE_ENV, API tokens — all unset.
grep -n 'export ' ~/.bashrc | grep -iE 'token|url|key|profile'
4. Permissions and non-executable scripts
The script is not executable, or is invoked without an interpreter, or writes to a directory cron’s user cannot touch.
/bin/sh: 1: /opt/jobs/backup.sh: Permission denied
5. The command genuinely fails
Sometimes exit status 1 is real: a curl got a non-2xx, a psql query errored, set -euo pipefail tripped on an unset variable. The environment is fine; the work failed.
How to Diagnose
Step 1 — Confirm cron actually ran the job and read the surrounding lines.
journalctl -t CRON --since '-1h' | grep -A0 -B1 'grandchild'
Jul 12 03:15:01 host CRON[21898]: (myuser) CMD (/opt/jobs/backup.sh)
Jul 12 03:15:01 host CRON[21897]: (CRON) error (grandchild #21899 failed with exit status 1)
On systems without journald, use the syslog file:
grep CRON /var/log/syslog | tail -20
Step 2 — Capture the job’s real output. Cron threw the stderr away; make the job keep it by redirecting to a log file in the crontab line.
* * * * * /opt/jobs/backup.sh >> /var/log/backup.log 2>&1
tail -20 /var/log/backup.log
/opt/jobs/backup.sh: line 4: aws: command not found
That single line usually names the cause — here, aws is not on cron’s PATH.
Step 3 — Reproduce cron’s minimal environment. Run the command the way cron does, with a scrubbed environment, so you see the same failure interactively.
env -i HOME="$HOME" LOGNAME="$LOGNAME" PATH=/usr/bin:/bin SHELL=/bin/sh \
/bin/sh -c '/opt/jobs/backup.sh'; echo "exit=$?"
/opt/jobs/backup.sh: line 4: aws: command not found
exit=127
If it fails here but works in your normal shell, the problem is environment (PATH/env), not logic.
Fixes
Set an explicit PATH at the top of the crontab or the script.
PATH=/usr/local/bin:/usr/bin:/bin
* * * * * /opt/jobs/backup.sh >> /var/log/backup.log 2>&1
Or inside the script, where it is version-controlled with the code:
#!/usr/bin/env bash
set -euo pipefail
export PATH="/usr/local/bin:/usr/bin:/bin"
Use absolute paths for everything — the interpreter, binaries, and data files. Do not rely on PWD. If the script must run from a directory, cd there explicitly first:
cd /opt/jobs || exit 1
source ./config.env
Source the environment the job needs instead of assuming login files ran:
set -a
. /opt/jobs/backup.env # DATABASE_URL, AWS_PROFILE, tokens
set +a
Make the script executable and correctly shebanged.
chmod +x /opt/jobs/backup.sh
head -1 /opt/jobs/backup.sh # -> #!/usr/bin/env bash
Wrap the job so failures are visible. Log start, end, and exit status so the next failure is self-explanatory:
#!/usr/bin/env bash
exec >>/var/log/backup.log 2>&1
echo "=== $(date -Is) start ==="
/opt/jobs/_backup-impl.sh
rc=$?
echo "=== $(date -Is) end rc=$rc ==="
exit "$rc"
What to Watch Out For
exit status 127means “command not found” (PATH),126means “found but not executable/permission”,1and2are the command’s own errors — the number narrows the cause.- Cron’s shell is
/bin/sh(often dash), not bash. Bashisms ([[ ]], arrays,source) fail undersh. SetSHELL=/bin/bashin the crontab or use a bash shebang and invoke the script directly. - Redirecting to a log is not optional for cron jobs — without it you are debugging blind. Always append
>> logfile 2>&1. set -euo pipefailmakes a script exit 1 on the first unset variable or failed pipe; a variable that exists interactively but not under cron will trip it.- The grandchild PID changes every run; do not treat
#NNNNas a stable identifier.
Related Guides
- Cron Error: ‘(CRON) info (No MTA installed, discarding output)’
- systemd Timer Service Failed With Result ‘exit-code’
- AI-Assisted Cron and Scheduled Job Cleanup
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.