WorkflowRadar

Feature

A cron that didn't fire isn't a failure n8n sees.

Native stale-workflow detection waits weeks before it cares. A daily invoice sync that broke yesterday is invisible for 13 more days under a 14-day stale window — 29 more under n8n's default 30. We compute the next expected fire time for every scheduled workflow and alert when it slips past a 2× grace window.

The failure mode

A scheduled workflow that doesn't fire is a different shape of bug from one that fires and errors. Nothing throws because nothing ran. Nothing surfaces in the executions log for the same reason. Native stale-detection waits for the workflow to be idle for a long time — typically 14 days minimum, 30 days on n8n native — before treating it as suspicious. For a daily cron that broke yesterday, that's two-to-four weeks of silent failure.

The common causes aren't exotic: a misconfigured cron node after a credential refresh, an n8n instance that lost its scheduler on a restart, a scheduleTrigger that someone toggled inactive during testing and forgot to flip back. All of them look identical from the outside — no execution rows, no errors, no signal.

How we detect it

At every sync we compute the next expected run time for each active scheduled workflow and persist it. Two trigger shapes are covered:

  • Cron expressions (the cron node and scheduleTrigger nodes configured with raw cron syntax) — parsed by croner, UTC.
  • scheduleTrigger interval config (the hourly / daily / weekly / monthly dropdown) — derived arithmetically from the interval + anchor hour / minute / day-of-week / day-of-month.

When we evaluate alerts, a workflow is flagged when now - next_expected_run_at > max(2 × interval, 24h) and there's been no execution at or after the expected time. Practical examples:

  • Hourly cron didn't fire in 2+ hours → alert.
  • Daily cron hits the 24-hour cap → alert (we don't wait 48 hours for daily workflows).
  • Weekly workflow — same 24-hour cap. We don't wait two weeks to surface it.

Sync-self-delay guard

If our own sync infrastructure had an outage — Vercel cron paused, a worker hung, a deployment took longer than expected — every scheduled workflow would look late at once when sync resumes. That's an alert storm, not a useful signal. Before we emit any missed-schedule alert we check the sync_runs table for a successful sync in the last hour. If we don't see one, we suppress the whole batch and log a single diagnostic line. The next sync that lands inside the threshold picks up where we left off.

A per-workspace cap of 20 missed-schedule alerts per dispatch is the belt-and-suspenders companion: even when the sync-self-delay guard somehow misses a case, no single workspace can flood a Slack channel.

What it looks like

Missed schedules are a distinct alert type. They route to email and to your Slack webhook just like failures do. The alert headline reads e.g. "Stripe customer sync — missed scheduled run"; the detail line shows how late the run is and the schedule it was expected to follow (47m past expected run (0 9 * * *)).

v1 honesty

Two limitations worth knowing about up front:

  • Multi-schedule workflows: if a workflow has more than one schedule node, we evaluate the first one. Single-schedule (the common case) is fully covered.
  • UTC only: cron expressions are parsed in UTC. n8n's instance timezone isn't captured via the public API today. If your cron is configured in a non-UTC zone with a meaningful time-of-day anchor, the expected fire time is offset by your zone delta. Bridging that is its own roadmap item.

Available on

Missed-schedule detection is on every paid tier — see pricing. Opt-out toggle in workspace alert settings if you want it off.