deactivated workflows keep running — schedule registrations are never cleaned up
Describe the problem/error/question
Schedule Trigger registrations are not cleaned up. A single Schedule Trigger fires more than once per scheduled time, and a registration survives deactivating the workflow — a workflow with active: false continues to execute on schedule, indefinitely.
The workflow record itself saves correctly (verified through the public API), so this is not a persistence problem. The evidence below suggests each activation adds a schedule registration while deactivating removes at most one, leaving the remainder firing.
Evidence
One workflow, one Schedule Trigger set to triggerAtHour: 6, timezone: Etc/UTC. Activated
2026-09-22T06:21:26Z. Three executions followed, all mode: trigger (none user-started):
id startedAt (UTC) mode status workflow state at the time
2735222 2026-09-23 06:00:42 trigger success active
2735225 2026-09-23 06:00:56 trigger success active
2755546 2026-09-24 06:00:56 trigger success INACTIVETwo things to note:
- 2026-09-23 double-fired — two executions 14 seconds apart from a single once-daily Schedule Trigger, on the very first scheduled day after activation.
- The workflow was deactivated at
2026-09-23T08:06:37Zand still fired the next morning. The:56registration survived deactivation; the:42one did not. The second-offset is preserved exactly across days, which is what suggests these are two distinct registrations rather than a retry.
Workflow state confirmed through the API immediately after the 2026-09-24 execution:
active: false
isArchived: false
updatedAt: 2026-09-23T08:06:37.179Z (unchanged since the deactivation)So the record says inactive, has said so for 22 hours, and the schedule fired anyway.
Archiving does not stop a schedule either
Separately, we found that archiving a workflow does not stop its Schedule Trigger. The workflow disappears from the workflow list but keeps firing. This is easy to be caught out by, because archiving reads as a "stop" action and the workflow is no longer visible to check.
Deleting the workflow outright is the only way we found to actually stop a running job. Neither deactivating nor archiving does it.
What appears to create the duplicate registration
The duplicate appears after updating an existing workflow by re-importing over it — importing a workflow file into a workflow that already exists, rather than creating a new one. The previously registered schedule is not replaced, so the workflow ends up carrying both the old and the new registration and fires once per registration.
What we expected
- One Schedule Trigger fires once per scheduled time.
- Deactivating a workflow removes all of its schedule registrations.
- Archiving a workflow stops it from running.
Impact
Both behaviours are dangerous for a workflow with external side effects.
Our workflow authenticates to a production API and writes to a shared spreadsheet. The 2026-09-24 execution — the one that ran while deactivated — minted a production access token, read the spreadsheet and parsed 18 rows before stopping. It happened to write nothing only because there was no outstanding work that morning. Had there been, a workflow we had explicitly deactivated would have performed production writes on real customer accounts.
The duplicate fire has the same shape: without mitigation, every scheduled run's side effects happen twice.
We have had to build our own idempotency and kill-switch logic inside the workflow to make scheduled execution safe — a once-per-day lock using $getWorkflowStaticData, plus a time-window check and a manual enable flag in a Code node, because Code node edits are the only control we found that reliably takes effect. The once-per-day lock is the only reason the 2026-09-23 double fire was harmless: execution 2735225 was stopped after 2 nodes.
Steps to reproduce
- Create a workflow with a single Schedule Trigger at a specific hour and minute, plus one node that records the time.
- Activate it. Let it fire on schedule.
- Edit the workflow by importing a workflow file over it (update the existing workflow rather than creating a new one), then activate again.
- Check the executions list filtered to that workflow at the next scheduled time — observe more than one execution, seconds apart, all
mode: trigger. - Deactivate the workflow. Confirm
active: falseviaGET /api/v1/workflows/{id}. - Wait for the next scheduled time.
Observed at step 6: a new execution appears, mode: trigger, while the workflow is inactive. Deleting the workflow is the only thing that stops it.
For the archiving variant: activate a scheduled workflow, confirm it fires, archive it, wait for the next scheduled time. Observed: it still fires.
What we ruled out
- Not a persistence problem.
activeandupdatedAtboth update correctly on deactivate, confirmed throughGET /api/v1/workflows/{id}. - Not duplicate workflows. The instance has three workflows in total, verified through the API. No second copy exists on a similar schedule.
- Not user-triggered. Every execution above reports
mode: trigger. - Not the durable scheduler. That feature does not exist in this version.
What is the error message (if any)?
None. Every operation reports success. The duplicate and the post-deactivation executions are all green. Nothing in the UI distinguishes them from a legitimate run — the executions list shows a normal successful execution for a workflow that the same UI shows as inactive.
The only way we found any of this was by querying the executions API and comparing timestamps against the deactivation time.
Please share your workflow/screenshots/recording
The behaviour does not appear specific to our workflow — any Schedule Trigger should reproduce it. A minimal example:
{
"nodes": [
{
"parameters": { "rule": { "interval": [ { "triggerAtHour": 6 } ] } },
"id": "11111111-1111-1111-1111-111111111111",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [0, 0]
},
{
"parameters": { "mode": "runOnceForAllItems", "jsCode": "return [{ json: { ranAt: new Date().toISOString() } }];" },
"id": "22222222-2222-2222-2222-222222222222",
"name": "Code",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [200, 0]
}
],
"connections": {
"Schedule Trigger": { "main": [ [ { "node": "Code", "type": "main", "index": 0 } ] ] }
},
"settings": { "executionOrder": "v1", "timezone": "Etc/UTC" }
}Our real workflow is not included because it holds production credentials in a Code node. It is otherwise an ordinary linear workflow: Schedule Trigger → Code → HTTP Request nodes.
Share the output returned by the last node
All three executions ran correctly. The problem is that two of them should never have started.
Node-level detail:
2735222 2026-09-23 06:00:42 -> 06:00:47 10 nodes, full chain (active)
2735225 2026-09-23 06:00:56 -> 06:00:56 2 nodes, stopped by our own
once-per-day guard (active)
2755546 2026-09-24 06:00:56 -> 06:00:58 10 nodes, full chain,
minted a production token
and read external data (INACTIVE)Debug info
core
- n8nVersion: 1.123.6
- platform: docker (self-hosted)
- nodeJsVersion: 22.21.0
- nodeEnv: production
- database: postgres
- executionMode: scaling (single-main)
- concurrency: -1
- license: enterprise (production)
storage
- success: all
- error: all
- progress: false
- manual: true
- binaryMode: filesystem
pruning
- enabled: true
- maxAge: 336 hours
- maxCount: 10000 executions
client
- userAgent: mozilla/5.0 (macintosh; intel mac os x 10_15_7) applewebkit/537.36 (khtml, like gecko) chrome/153.0.0.0 safari/537.36
Generated at: 2026-09-23T07:28:35.474Z
Source: n8n-io/n8n