Back to Blog
Tutorial

Why Is My Cron Job Not Running? The 7 Most Common Errors and How to Fix Them

Your cron job works manually but fails in cron? Here's why—and exactly how to fix it. Troubleshoot the 7 deadliest cron errors in minutes, not hours.

10 min read
By Cron Generator Team

Why Is My Cron Job Not Running? The 7 Most Common Errors and How to Fix Them

Your script works perfectly when you run it manually. You add it to cron. You wait. Nothing happens.

No output. No errors. No results. Just... silence.

Sound familiar? You're not alone. Cron jobs fail silently for everyone. The difference is knowing where to look and what to fix.

This guide covers the 7 most common reasons cron jobs fail—ranked by how often they actually happen—plus the exact commands to diagnose and fix each one.

Let's debug your cron job.

Error #1: Wrong File Paths (The #1 Killer)

The Problem

This happens 60% of the time. Your script works manually because your shell knows where everything is. Cron doesn't.

When you run ./backup.sh, your shell uses your $PATH to find executables. Cron runs with a minimal environment and can't find anything that isn't specified with an absolute path.

Symptoms

# Your cron job
0 2 * * * backup.sh

# What you see
Nothing. No output. No errors. Script never runs.

The Fix

Use absolute paths for EVERYTHING:

Wrong:

0 2 * * * backup.sh
0 2 * * * python script.py
0 2 * * * mysqldump database > backup.sql

Right:

0 2 * * * /usr/local/bin/backup.sh
0 2 * * * /usr/bin/python3 /home/user/script.py
0 2 * * * /usr/bin/mysqldump database > /var/backups/backup.sql

How to Find Absolute Paths

# Find where a command is located
which python3
# Output: /usr/bin/python3

which mysqldump
# Output: /usr/bin/mysqldump

which node
# Output: /usr/local/bin/node

Quick Test

Before adding to cron, test with cron's environment:

# Run with minimal environment (like cron does)
env -i HOME=$HOME /usr/local/bin/backup.sh

# If it fails here, it will fail in cron

Pro Tip: Set PATH in Crontab

Add this to the top of your crontab:

PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash

0 2 * * * /usr/local/bin/backup.sh

This gives cron a better PATH, but still use absolute paths for scripts.


Error #2: Permission Denied (The Silent Killer)

The Problem

Your script isn't executable. Cron tries to run it, gets "permission denied," and gives up silently.

Symptoms

# Check your cron logs
grep CRON /var/log/syslog

# You see
CRON[1234]: (user) CMD (/home/user/backup.sh)
# But nothing happens

The Fix

Make your script executable:

# Check current permissions
ls -l /home/user/backup.sh
# Output: -rw-r--r-- (NOT executable)

# Fix it
chmod +x /home/user/backup.sh

# Verify
ls -l /home/user/backup.sh
# Output: -rwxr-xr-x (Now executable)

The Right Permissions

# For scripts
chmod 755 /path/to/script.sh
# Owner: read, write, execute
# Others: read, execute

# For scripts with sensitive data
chmod 700 /path/to/script.sh
# Only owner can read/write/execute

Check File Ownership

# See who owns the file
ls -l /home/user/backup.sh

# If owned by root but running as user
sudo chown user:user /home/user/backup.sh

Don't Forget Shebang

Start every script with:

#!/bin/bash
# or
#!/usr/bin/env python3
# or
#!/usr/bin/env node

Without this, cron doesn't know how to execute your script.


Error #3: Environment Variables Missing

The Problem

Your script relies on environment variables that exist in your shell but not in cron's minimal environment.

Symptoms

Script works manually:

$ ./backup.sh
✓ Database backed up successfully

Same script in cron:

# Cron log shows
mysqldump: command not found
# or
Error: API_KEY not set

The Fix

Option 1: Set variables in crontab

# At the top of your crontab
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
HOME=/home/user
API_KEY=your_key_here

0 2 * * * /usr/local/bin/backup.sh

Option 2: Source environment in script

#!/bin/bash

# Load environment variables
source /home/user/.bashrc
# or
source /home/user/.env

# Now your script has access to all variables
/usr/bin/mysqldump...

Option 3: Set variables in the cron job

0 2 * * * API_KEY=xyz DB_USER=admin /usr/local/bin/backup.sh

Debug: Check What Cron Sees

Create a test cron job to see the environment:

# Add to crontab
* * * * * env > /tmp/cron-env.txt

# Wait one minute, then check
cat /tmp/cron-env.txt

# Compare to your normal environment
env > /tmp/normal-env.txt
diff /tmp/cron-env.txt /tmp/normal-env.txt

You'll be shocked how little cron has access to.

Common Missing Variables

  • PATH - Where to find executables
  • USER - Current username
  • HOME - Home directory
  • LANG - Language settings
  • API keys and credentials
  • Database passwords
  • Custom application variables

Error #4: Cron Syntax Errors

The Problem

Your cron expression is malformed. Cron silently ignores the entire line.

Symptoms

# You added this to crontab
0 2 * * * 1-5 /usr/local/bin/backup.sh

# Nothing happens. No errors in logs.

Common Syntax Mistakes

1. Wrong field count

# Wrong (6 fields - one too many)
0 2 * * * 1-5 /usr/local/bin/backup.sh

# Right (5 fields + command)
0 2 * * 1-5 /usr/local/bin/backup.sh

2. Invalid range

# Wrong (hour 25 doesn't exist)
0 25 * * * /usr/local/bin/backup.sh

# Right
0 23 * * * /usr/local/bin/backup.sh

3. Typos in special characters

# Wrong (using | instead of /)
*/15 * * * * /usr/local/bin/check.sh

# Right
*/15 * * * * /usr/local/bin/check.sh

4. Invalid day/month combinations

# Wrong (February 31st doesn't exist)
0 0 31 2 * /usr/local/bin/monthly.sh

# Be careful with day-of-month + month combinations

The Fix

Before adding to crontab, validate your syntax:

  1. Use our cron expression validator - Paste your expression and see if it's valid

  2. Check with cron syntax checker:

# Many modern systems validate on save
crontab -e
# If there's a syntax error, you'll see a warning
  1. Test with a simple command first:
# Add a test job that runs every minute
* * * * * echo "It works!" >> /tmp/crontest.txt

# Wait a minute, check the file
cat /tmp/crontest.txt
# If you see output, cron is working

Quick Syntax Reference

# Field order
* * * * * command
│ │ │ │ │
│ │ │ │ └─ Day of Week (0-6, Sunday=0)
│ │ │ └─── Month (1-12)
│ │ └───── Day of Month (1-31)
│ └─────── Hour (0-23)
└───────── Minute (0-59)

# Valid examples
*/15 * * * *              # Every 15 minutes
0 */6 * * *               # Every 6 hours
0 9-17 * * 1-5            # Business hours, weekdays
0 0 1,15 * *              # 1st and 15th of month

Error #5: Output Not Being Captured

The Problem

Your cron job IS running, but you can't see the output. You assume it's not working.

Symptoms

# Cron log shows it ran
grep CRON /var/log/syslog
# Output: CRON[1234]: (user) CMD (/usr/local/bin/backup.sh)

# But you don't see any results

The Fix

Redirect output to a log file:

# Capture both stdout and stderr
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

# Only capture errors
0 2 * * * /usr/local/bin/backup.sh 2>> /var/log/backup-errors.log

# Separate logs for stdout and stderr
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>> /var/log/backup-error.log

Understanding redirections:

  • >> - Append to file (don't overwrite)
  • > - Overwrite file
  • 2>&1 - Redirect stderr (errors) to stdout
  • 2>> - Append errors to file
  • > /dev/null 2>&1 - Discard all output

Enable Email Notifications

Cron can email you output:

# At top of crontab
MAILTO=your-email@example.com

# Now cron will email you any output
0 2 * * * /usr/local/bin/backup.sh

Disable email:

MAILTO=""
# or redirect to null
0 2 * * * /usr/local/bin/backup.sh > /dev/null 2>&1

Add Logging to Your Script

#!/bin/bash

LOG_FILE="/var/log/backup.log"

echo "[$(date)] Starting backup..." >> $LOG_FILE

# Your backup commands
if /usr/bin/mysqldump database > backup.sql; then
    echo "[$(date)] Backup successful" >> $LOG_FILE
else
    echo "[$(date)] ERROR: Backup failed!" >> $LOG_FILE
    exit 1
fi

echo "[$(date)] Backup complete" >> $LOG_FILE

Check if Job Actually Ran

# View recent cron executions
grep CRON /var/log/syslog | tail -20

# On some systems
journalctl -u cron -n 50

# See what user's cron jobs ran
grep "CRON\[.*\]: (user)" /var/log/syslog

Error #6: Cron Service Not Running

The Problem

The cron daemon isn't running at all. None of your cron jobs will execute.

Symptoms

# Your crontab looks fine
crontab -l
# Shows your jobs

# But nothing ever runs
# No entries in /var/log/syslog

The Fix

Check if cron is running:

# On systemd systems (Ubuntu, Debian, CentOS 7+)
systemctl status cron
# or
systemctl status crond

# On older systems
service cron status

If it's not running, start it:

# Start cron
sudo systemctl start cron

# Enable it to start on boot
sudo systemctl enable cron

# Verify it's running
systemctl is-active cron

On macOS:

# Check if cron is loaded
sudo launchctl list | grep cron

# Load cron if needed
sudo launchctl load -w /System/Library/LaunchDaemons/com.vix.cron.plist

Check Cron Logs

# Ubuntu/Debian
tail -f /var/log/syslog | grep CRON

# CentOS/RHEL
tail -f /var/log/cron

# Check if any cron jobs have run recently
grep CRON /var/log/syslog | tail -50

Verify Cron Daemon

# See if crond process is running
ps aux | grep cron

# Should show something like:
# root     1234  0.0  0.0  /usr/sbin/cron -f

Error #7: Script Works in Terminal, Fails in Cron

The Problem

Your script runs perfectly when you execute it manually but fails mysteriously in cron. This is the most frustrating error because everything LOOKS right.

Why This Happens

When you run a script manually:

  • Your shell loads .bashrc, .bash_profile, etc.
  • You have a full environment with all variables
  • Your PATH includes everything
  • You're running from a specific directory

When cron runs your script:

  • Minimal environment (no .bashrc loading)
  • Limited PATH
  • Different working directory
  • Different user (sometimes)

Symptoms

# Works manually
$ /usr/local/bin/backup.sh
✓ Success!

# Fails in cron
0 2 * * * /usr/local/bin/backup.sh
# No output, or errors in log

The Fix

1. Simulate cron's environment:

# Run with cron's minimal environment
env -i HOME=$HOME SHELL=/bin/sh /usr/local/bin/backup.sh

# If it fails here, you found the problem

2. Make script self-contained:

#!/bin/bash

# Set environment explicitly
export PATH=/usr/local/bin:/usr/bin:/bin
export HOME=/home/user
cd /home/user/project || exit 1

# Load any needed environment variables
if [ -f /home/user/.env ]; then
    source /home/user/.env
fi

# Now run your commands
/usr/bin/python3 script.py

3. Use absolute paths everywhere:

#!/bin/bash

# Wrong
mysql -u root database < backup.sql

# Right
/usr/bin/mysql -u root database < /var/backups/backup.sql

4. Specify working directory in cron:

# Change to directory before running
0 2 * * * cd /home/user/project && /usr/local/bin/backup.sh

# Or set it in the script
0 2 * * * /usr/local/bin/backup.sh

5. Debug by comparing environments:

Create a debug script:

#!/bin/bash
echo "PATH: $PATH"
echo "HOME: $HOME"
echo "USER: $USER"
echo "PWD: $PWD"
echo "SHELL: $SHELL"
which python3
which mysql
ls -la

Run it manually and via cron:

# Manual
./debug.sh > /tmp/manual-env.txt

# Cron
* * * * * /path/to/debug.sh > /tmp/cron-env.txt

# Compare
diff /tmp/manual-env.txt /tmp/cron-env.txt

The differences tell you what's missing.


Bonus: Advanced Debugging Techniques

Enable Detailed Logging

Wrap your entire script in verbose mode:

#!/bin/bash
set -x  # Print every command before executing
set -e  # Exit on any error

# Your script here
/usr/bin/mysqldump...

Test Cron Every Minute

While debugging, make the job run every minute:

# Change this:
0 2 * * * /usr/local/bin/backup.sh

# To this temporarily:
* * * * * /usr/local/bin/backup.sh >> /tmp/debug.log 2>&1

# Check log after 1-2 minutes
tail -f /tmp/debug.log

# Once working, change back to correct schedule

Create a Wrapper Script

Wrap complex commands in a script that handles all environment setup:

#!/bin/bash
# /usr/local/bin/cron-wrapper.sh

# Setup environment
export PATH=/usr/local/bin:/usr/bin:/bin
source /home/user/.bashrc

# Enable logging
exec 1> >(logger -s -t $(basename $0)) 2>&1

# Run the actual command
"$@"

Use it in cron:

0 2 * * * /usr/local/bin/cron-wrapper.sh /home/user/backup.sh

Check System Resources

Sometimes cron jobs fail due to resource limits:

# Check available disk space
df -h

# Check memory
free -h

# Check CPU load
uptime

# Check if system is swapping
vmstat 1 5

The Ultimate Cron Debugging Checklist

When your cron job doesn't work, check these in order:

1. Is cron running?

systemctl status cron

2. Is your cron syntax valid?

3. Does the cron job appear in logs?

grep CRON /var/log/syslog | grep username

4. Is the script executable?

ls -l /path/to/script.sh
chmod +x /path/to/script.sh

5. Are you using absolute paths?

which command-name
# Use the full path shown

6. Can you run it manually?

/path/to/script.sh

7. Can you run it with cron's environment?

env -i HOME=$HOME /path/to/script.sh

8. Is output being captured?

# Add logging
0 2 * * * /path/to/script.sh >> /var/log/script.log 2>&1

9. Are environment variables set?

# Add to crontab:
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash

10. Are there any missing dependencies?

# Check if all commands exist
which python3
which mysqldump
# etc.

Quick Fixes That Solve 90% of Problems

Most cron issues come down to these three fixes:

Fix #1: The Minimal Working Template

Use this template for ALL cron jobs:

# Environment
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com

# Job with full logging
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Fix #2: The Bulletproof Script Header

Start every script like this:

#!/bin/bash
set -e  # Exit on error
set -u  # Exit on undefined variable
set -o pipefail  # Catch errors in pipes

# Set environment
export PATH=/usr/local/bin:/usr/bin:/bin
cd "$(dirname "$0")" || exit 1

# Your code here

Fix #3: The Debug Command

When all else fails, capture EVERYTHING:

* * * * * /path/to/script.sh >> /tmp/debug.log 2>&1 || echo "Exit code: $?" >> /tmp/debug.log

This shows:

  • All output
  • All errors
  • Exit code if it fails

Still Not Working?

If you've tried everything and your cron job still won't run:

Use Our Tools

  1. Validate your cron syntax with our cron generator

    • Build expressions visually
    • See exactly when they'll run
    • No syntax errors
  2. Decode existing expressions with our decoder

    • Paste your cron string
    • Get human-readable explanation
    • Verify it does what you think

Get Professional Help

Check these resources:

  • Your system logs: /var/log/syslog or /var/log/cron
  • Cron documentation: man crontab
  • Stack Overflow with your specific error message

Nuclear Option: Start Over

Sometimes it's faster to rebuild than debug:

  1. Remove the problematic job: crontab -e
  2. Test your script manually until it works perfectly
  3. Add it back with proper logging
  4. Monitor the logs to verify execution

Prevent Future Problems

Before Adding Any Cron Job:

  1. ✅ Test the script manually
  2. ✅ Test with minimal environment (env -i)
  3. ✅ Use absolute paths everywhere
  4. ✅ Add comprehensive logging
  5. ✅ Validate the cron syntax
  6. ✅ Start with frequent execution (every minute)
  7. ✅ Monitor logs to verify it works
  8. ✅ Change to actual schedule once confirmed

Standard Script Template

Use this template for reliable cron scripts:

#!/bin/bash

# Exit on any error
set -euo pipefail

# Configuration
LOG_FILE="/var/log/$(basename $0 .sh).log"
LOCK_FILE="/tmp/$(basename $0 .sh).lock"

# Logging function
log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

# Prevent concurrent execution
if [ -f "$LOCK_FILE" ]; then
    log "ERROR: Script already running"
    exit 1
fi
trap "rm -f $LOCK_FILE" EXIT
touch "$LOCK_FILE"

# Main script
log "Starting..."

# Your commands here
if /usr/bin/command1; then
    log "Command 1 successful"
else
    log "ERROR: Command 1 failed"
    exit 1
fi

log "Complete"

This handles:

  • Logging with timestamps
  • Error handling
  • Lock file to prevent overlaps
  • Automatic cleanup

Your cron job should now be running. If it's still not working after checking all seven errors, the problem is probably something unique to your setup—time to dive into those logs.

Need to create a cron job without the debugging headache? Use our visual generator to build perfect expressions every time.

Stop debugging. Start automating.


Related Articles

Dive Deeper into Cron:

Build Better Cron Jobs:

Practical Examples: