Missed-Run Semantics: What Your AI Cron Job Should Do After the Server Was Asleep
Every self-hosted automation setup has a boring moment that nobody designs for: the machine was off at 8:00 AM.
Maybe the Raspberry Pi rebooted after an update. Maybe the power flickered. Maybe the gateway was wedged for forty minutes and the scheduler never fired. Either way, the question arrives the moment everything comes back online: what should the job that was supposed to run at 8:00 do now?
Most people never answer this on purpose. The default answer is whatever the scheduler happens to do, and that default is different for every tool. Some silently skip. Some fire everything that was missed at once. Some fire only the most recent one. Your agent inherits that behavior without anyone ever deciding it was acceptable.
For a plain shell script, a wrong guess costs you a duplicate backup. For an AI agent that sends emails, posts content, or spends money, a wrong guess is a real incident. This post is about making the decision deliberately.
Three Possible Behaviors
When a scheduled run is missed, there are really only three things a job can do.
Skip. The window passed, so the run is gone. The next scheduled run proceeds normally. Nothing is made up.
Catch up once. The system notices it missed a run, performs exactly one run as soon as it can, and then returns to the normal schedule. Five missed hourly runs collapse into one.
Replay all. Every missed occurrence executes, in order. Five missed hourly runs become five back-to-back runs.
None of these is correct in general. The right one depends on what the job does, and you can classify most jobs in about ten seconds once you ask the right question.
The Question: Is This Job About a Moment or a State?
A job that is about a moment only has value at a specific time. A “good morning, here is your calendar” briefing at 7:30 is pointless at 2:00 PM. A reminder to leave for a meeting is worse than pointless after the meeting started. Skip is correct here, or at most a late run that is clearly labeled as late.
A job that is about a state reconciles the world to match a desired condition. Syncing a CRM, refreshing a cached report, checking whether any invoices went overdue, pruning old logs: running late is fine, and running once is enough. Catch up once is correct here, because the output depends on the data as it is now, not on the clock.
A job that is about every event in a sequence needs each occurrence handled. A queue consumer, a per-day ledger entry, a nightly export that must exist for each calendar date for auditing. Replay is correct, but only if each run is parameterized by the date it represents rather than by “now.”
Most agent jobs are states, a few are moments, and very few truly need replay. If you are not sure, you probably have a state job, and catch up once is the safe default.
Where AI Agents Make This Worse
Traditional cron jobs are at least deterministic. Replaying one is predictable. An agent job adds three complications.
Agents act on “today” implicitly. A prompt like “write today’s summary” quietly depends on the current date. A replayed run on the wrong day writes the wrong summary, labeled confidently. If a job can be replayed, pass the target date in explicitly instead of letting the model infer it.
Side effects are not idempotent by default. Replaying a content job might publish three posts in a row. Replaying a follow-up job might email the same lead three times. This is the same family of problem as retry safety, and the fix is the same: give every run a key built from the job and the window it represents, and check it before acting.
Cost multiplies. Five replayed runs of a research agent are five bills. A restart after an outage should not become a surprise token spike.
A Practical Policy You Can Write Down
For each scheduled job, record four lines next to the job definition.
- Type: moment, state, or sequence.
- On missed run: skip, catch up once, or replay.
- Max lateness: how late a catch-up run is still allowed to be useful. A daily briefing might allow ninety minutes. A weekly report might allow a full day.
- Run key: the string that makes a repeated run harmless, such as
weekly-report:2026-W41.
The max-lateness line does most of the work. It turns “should it run?” into a simple comparison: current time minus scheduled time. If that gap exceeds the limit, the job logs a skip with the reason and exits cleanly. Skipping on purpose, with a log line, is very different from skipping because nothing noticed.
Stagger The Recovery
Even when every job is configured correctly, a reboot has one more trap. Ten jobs that were each “catch up once” all become due at the same instant the system boots. On a small machine that means ten model calls, several browser sessions, and a pile of network requests competing at once, right when services are still warming up.
Add a small randomized delay, or a startup queue that releases catch-up runs one or two at a time. Order them by importance: the thing that guards your money or your customers first, the nice-to-have digest last.
Make The Skip Visible
The worst version of all of this is a skipped job that leaves no trace. You find out two weeks later that the Monday report never went out, and nothing in your logs explains why.
Whatever the decision, write it down. A single line is enough: job name, scheduled time, actual time, decision, reason. If your agent posts a daily status message, include a “skipped or late” section when it is non-empty. The point is that a missed run becomes a known, explained event instead of a silent hole.
A Quick Test You Can Run This Week
Pick your three most important scheduled jobs. For each one, answer honestly:
- If the host were off for six hours and came back, what would this job do right now?
- Would that behavior be acceptable if it happened at 3 AM while you were asleep?
- Would doing it twice cause harm?
If you cannot answer the first question without reading scheduler documentation, you have found your gap. Better yet, actually simulate it: stop the scheduler, move the clock past a run time, start it again, and watch. It takes ten minutes and tells you more than any amount of config reading.
The Takeaway
Scheduling is not just “when does it run.” It is also “what happens when it cannot run.” Classify each job as a moment, a state, or a sequence; pick skip, catch up once, or replay on purpose; cap how late is too late; and key every run so doing it twice is harmless.
Self-hosted automation earns trust not when everything works, but when a reboot at the wrong time leaves you with a clean, boring log line instead of a mess to clean up.
More from the build log
Suggested
Want the full MarketMai stack?
Get the core MarketMai guides and operator playbooks in one premium bundle for $49.
View Bundle