What it means
Cron starts each job with /bin/sh -c "<your command>". The shell searches the command in PATH, which cron sets to /usr/bin:/bin and nothing more. It does not read .bashrc, .profile or /etc/environment. On Debian and Ubuntu, /bin/sh is dash:
/bin/sh: 1: node: not foundOn RHEL and Fedora, /bin/sh is bash, and the same failure reads /bin/sh: line 1: node: command not found. Either way, you only see it if the output reaches you: without a mail server cron logs No MTA installed instead.
Common causes
1. The command is outside /usr/bin and /bin. /usr/local/bin (aws, docker-compose, binaries you installed by hand), /usr/sbin, ~/.local/bin (pip install --user), ~/.composer/vendor/bin. Find where it lives:
command -v aws2. A version manager. nvm, pyenv, rbenv and asdf add their directories to PATH from your shell profile, which cron never reads.
3. A bash builtin under dash. source does not exist in dash: /bin/sh: 1: source: not found. Same for [[, which fails with [[: not found.
4. Windows line endings. A crontab saved with CRLF gives a command ending in \r. The error prints as : not found, with the name hidden:
crontab -l | cat -A | grep '\^M'5. Wrong user. The job runs under a user that lacks the tool, or whose home holds a different version.
How to fix
- Test the line with cron's environment before reinstalling it:
env -i HOME="$HOME" PATH=/usr/bin:/bin /bin/sh -c 'aws s3 ls' - Use absolute paths, from
command -v:/usr/local/bin/aws s3 sync /backups s3://bucket. - Or set
PATHonce at the top of the crontab:PATH=/usr/local/bin:/usr/bin:/bin. Cron does not expand$PATHthere, so write the full list. - For bash syntax, write
.instead ofsource, or addSHELL=/bin/bashat the top of the crontab. Better, move the logic into a script with a#!/bin/bashline. - Convert line endings with
dos2unixand reinstall withcrontab file.
Is it just you?
If the job fires at times you did not expect, paste the expression into the cron expression generator to see its next runs.
Get alerted next time
- A command that is not found never reaches the ping chained after it with
&&, so a healthcheck alerts once the grace period passes. - Use the healthcheck with the job's own cron expression and timezone, so every expected run is checked.
- Call the URL with
/failappended from a wrapper script to alert at once, without waiting for the grace period.
FAQ
What is the default PATH in cron? ▼
/usr/bin:/bin for user crontabs, in both Debian's cron and cronie. /etc/crontab often sets a longer PATH on its first lines, which is why the same command can work there and fail in crontab -e.
How do I use nvm or pyenv in a cron job? ▼
Call the binary by its full path, which you get from command -v node or pyenv which python in your terminal, for example /home/deploy/.nvm/versions/node/v22.11.0/bin/node. Sourcing nvm.sh from cron also works, but only with SHELL=/bin/bash, and it breaks when you change versions.
Why do I see : not found with no command name? ▼
The crontab has Windows line endings. The command ends with a carriage return, which moves the cursor back to the start of the line when the error is printed and hides the name. Convert the file with dos2unix or sed -i 's/\r$//' and reinstall it.