Scheduled AI Agent Failures: What Breaks Differently From a Cron Job
We’ve argued that a scheduled agent run has the same operational shape as any other cron job: it needs a trigger, a place to run, a record of what happened, and a safe way to deploy changes. That leaves something out. A shell script either exits 0 or it doesn’t. An agent can exit 0, produce a plausible-looking result, and still have done the wrong thing, which means the usual runbook catches maybe half of what actually goes wrong once the thing running on your schedule is an LLM loop instead of pg_dump.
The rest of this post covers the failure modes specific to putting an agent behind a timer, roughly in the order to check them.
1. It exited 0 and did nothing useful
A typical cron failure is silence: the job didn’t run, or it errored loudly enough that something noticed. A scheduled agent can run, return a 200 or exit code 0, and still produce output that is wrong. An agent that was supposed to reconcile yesterday’s transactions can decide there was nothing to reconcile, format that as a clean success, and exit fine. Nothing in a standard cron monitor distinguishes that from an actual clean run, because from the trigger’s perspective, it was one.
Check: does your run record capture the agent’s actual output, not just its exit code? A pass/fail heartbeat is not enough for a job whose failure mode is “succeeded at the wrong thing.”
2. The prompt drifted, the schedule didn’t
Shell scripts don’t change between runs. Agents can, indirectly: a model version changes, a tool it calls updates its API, or someone tweaks a system prompt without knowing it’s wired to a 2am cron entry. The schedule fires as configured, but the behavior no longer matches what you tested three weeks ago.
Check: is there a diff-able record of what changed about the agent’s inputs (prompt, tools, model) between the last known-good run and now? If the answer is split between a chat history and a deploy log, correlating them after something breaks is a bad night.
3. It ran twice, or ran into itself
Ordinary idempotency problems get worse with agents because a second concurrent run doesn’t just duplicate a database write; it can duplicate an action with a side effect the agent decided to take: a second email sent, a second API call to a paid service, a second Slack message posted. Cron’s usual double-run causes (a job that overruns its own interval, a missed-then-caught-up trigger after a reboot) affect an agent the same way they affect a shell script, but the damage can be larger because an agent decides which steps to take.
Check: does the job itself lock against overlap (a lockfile, a flock, a check against its own last-run record), or do you assume the schedule alone prevents it? That assumption breaks the first time a run takes longer than the interval.
4. A looping run costs money
A stuck shell script wastes CPU time you already paid for. A stuck or looping agent run burns tokens on every retry and tool call, and it can keep doing that for a while before anything looks like a conventional failure. A job that used to cost cents can, after a tool it depends on starts returning malformed responses, cost real money before a human notices the pattern.
Check: is there a ceiling on what a single scheduled run can spend (a token budget, a max tool-call count, a wall-clock timeout), enforced by something other than the agent’s own judgment about when to stop?
5. It needed a machine-specific credential that isn’t there anymore
This one isn’t specific to agents, but it hits them harder because they tend to touch more APIs, keys, and scopes than a single-purpose script. A key rotates, a .env gets regenerated on redeploy, a credential that lived in one shell’s environment doesn’t exist in the one cron actually runs in. The run doesn’t fail with “auth error” as often as you’d hope; sometimes it fails with the agent working around the missing capability and doing something adjacent instead.
Check: run the job’s actual scheduled command by hand, in the same shell cron would use (not your interactive shell, which has a different, more complete environment), the same way you’d debug a silently-failing shell script. The usual runbook already covers this. It matters more when the job missing a credential can improvise.
What this changes about how you schedule the job
Your scheduling layer should keep a record of the output as well as the exit code, and enforce a hard ceiling on cost and concurrency that doesn’t rely on the agent noticing its own failure. norc’s job history already records the exit code and run duration for every reported run, plus a bounded tail of the output (the last 20 lines or 4,000 characters of combined stdout and stderr), regardless of what the command runs. That is enough to catch #1, #3, and #5 by hand; #2 and #4 are on you to instrument in the job itself, because no scheduler can see inside a model call it isn’t making.
If you’re putting an agent on a schedule for the first time, ask what the job does when it’s wrong and whether anything will notice.
FAQ
Does norc detect that an agent’s output was wrong, not just that it ran? No, and no scheduling tool can, since that requires domain knowledge about what “correct” looks like for your specific job. What norc gives you is the raw material to check: a bounded output tail per run (the last 20 lines or 4,000 characters), so “wrong but exit-0” is often visible in the history when the relevant output is in that tail, instead of inferred from downstream damage.
Should I set a timeout on every scheduled agent job?
Yes, if the tool you’re using it through will actually enforce one. Cron itself won’t kill a hung process for you. Wrap the command with timeout (or your agent framework’s own limit) so a hung run doesn’t sit there past the next scheduled trigger.
Is overlap-locking something norc handles, or the job?
The job. norc runs the command you give it on schedule; if that command needs to refuse to start a second copy of itself, that’s a flock or equivalent inside the script, the same as it would be for any long-running cron job.
norc keeps a history of what happened in every scheduled run, agent or otherwise, on whatever machine it ran on. That lets you check the output tail instead of treating “it exited fine” as “it did the right thing.”