Scheduled tasks dispatch ahead of their configured fire time (3 confirmed instances)
Summary
On three separate occasions, a Claude Code scheduled task fired significantly
before the time it was configured for — once ~95 days early, once ~3h40m
early, once ~27 days early (twice, on two different tasks). Each incident was
caught only because the task's own prompt happened to include a hand-written
guard checking the wall-clock time against its intended fire time. Nothing in
the scheduler itself appears to prevent early dispatch.
We've since added a defensive guard to every one of our scheduled tasks
(41 at last count) so this fails safe rather than fails silent — but that's a
workaround, not a fix, and it means every task author has to remember to
re-add it. We'd like to understand whether this is a known issue, and if not,
report it as a bug.
Environment
- Claude Code CLI, scheduled tasks feature (
create_scheduled_task/
list_scheduled_tasks / update_scheduled_task MCP tools)
- Tasks configured both as recurring (
cronExpression, 5-field, local time)
and one-off (fireAt, ISO 8601 with timezone offset)
- macOS host
The three confirmed instances
| Task | Configured fireAt / cron slot | Actually dispatched | Early by | What it would have done unguarded |
|---|---|---|---|---|
| A one-off task meant to shift ~32 cron expressions + 7 launchd plists for a DST fallback | 2026-11-01 02:00 (local) | 2026-07-28 19:42 (local) | ~95 days | Applied a DST correction to the entire fleet three months before the actual DST transition, leaving every one of those schedules an hour off for three months. |
| A one-off verification task meant to check a specific slot after it had run | 2026-07-29 04:45 (local) | 2026-07-29 01:05 (local) | ~3h 40m | Ran before the slot it was supposed to verify had actually occurred, and (without a guard) would have reported a fabricated "slot missed" failure — a false alarm generated by the checker running too early, not by any real problem. |
| A one-off evaluation task tied to a 30-day trial-cancellation decision window | 2026-08-23 16:00 UTC | 2026-07-27 23:44 PT | ~27 days | Ran a full evaluation and produced a real internal decision almost a month before the window it was scoped to evaluate — the task had no early-dispatch guard at the time, so nothing caught it. |
We have heartbeat logs (task-written completion records with timestamps) for
the first two on disk that corroborate the early dispatch times against the
task's own configured trigger. Happy to share redacted excerpts if useful.
Why this matters
These are all one-off tasks with an explicit fireAt (or an explicit
"don't run before X" business rule) — there's no ambiguity about when they
were supposed to run. The dispatcher fired them anyway, in each case with
no downtime, restart, or recovery event to explain it (we checked: no
other scheduled task or system event moved in that window). It doesn't
appear to be a "catch-up misfire after being offline" pattern — it looks like
the dispatcher can simply choose to run a task before its time.
The risk we're actually worried about isn't any of these three specific
tasks — it's that a scheduler that can fire a task 95 days early will
eventually fire something early that does something irreversible (a
destructive action, a financial decision, an external notification) with no
guard in front of it, because we can't guarantee every future task author
remembers to add one.
What we've done as a workaround
Every scheduled task we run now opens with a hand-written guard: read the
current time, compare it against the task's own declared fireAt (or cron
window), and exit immediately without doing any work if dispatched too
early. This has worked 3-for-3 in the incidents above (once retrofitted) and
we've also had to fix a second-order bug where a guard's own "I skipped
because it's too early" log entry was being misread by our own tooling as
"the task already ran" — which could have caused a future legitimate
dispatch to be silently skipped as a false duplicate. Both issues are
contained on our end now, but the underlying early-dispatch behavior is
still there and still unexplained.
Questions for Anthropic
- Is early dispatch (ahead of a configured
fireAtor cron slot) a known
behavior of the scheduled-tasks system? Is there a legitimate reason a
task might be dispatched before its configured time (e.g. a jittered
"best-effort" window, a catch-up mechanism after some kind of internal
restart)?
- If this is unexpected, we'd like to report it as a bug. What's the best
way to do that, and what additional diagnostic information would be
useful (task IDs, exact configured vs. observed timestamps, etc.)?
- Is there a recommended pattern for a task to reliably determine "was I
dispatched at/after my intended time" from within the task itself, other
than what we've built?
Thanks — happy to provide more detail on any of the above.