Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Automation By James Joyner IV · · 8 min read

Cron Error: '(CRON) error (grandchild #NNNN failed with exit status 1)' — Cause, Fix, and Troubleshooting Guide

Quick answer

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
Free toolkit

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 CRON or /var/log/syslog shows (CRON) error (grandchild #NNNN failed with exit status 1) right after a CMD (...) 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 127 means “command not found” (PATH), 126 means “found but not executable/permission”, 1 and 2 are the command’s own errors — the number narrows the cause.
  • Cron’s shell is /bin/sh (often dash), not bash. Bashisms ([[ ]], arrays, source) fail under sh. Set SHELL=/bin/bash in 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 pipefail makes 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 #NNNN as a stable identifier.
Free download · 368-page PDF

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.