PSA: n8n's Continue On Fail silently swallows node errors — your execution log lies to you

Something I see constantly in production n8n workflows that trips people up.

When you enable Continue On Fail on a node, n8n will mark the execution as success even if that node failed with a 500, a 401, or an empty response. The error gets passed downstream as error output data, the workflow keeps running, and the top-level execution status shows green.

This means: your HubSpot node failed, your CRM row was never created, your client never got the email — and n8n’s execution log shows success.

Three places this causes real pain:

  1. HTTP Request nodes with Continue On Fail — a 500 from an API looks identical to a 200 in the execution view

  2. Code nodes that throw errors — silently passed downstream

  3. Any node in a multi-step flow where you “just want it to keep going”

The fix is to add an explicit IF node after any critical node that uses Continue On Fail — check for the presence of $json.error in the output and route it to an alert branch.

Has anyone else hit this in production? Curious how people are handling it across larger workflows.

good additions. the inconsistent error shape trips people up constantly. handling both $json.error.message and $json.error as a plain string in the same IF check is the right call.

the zero items case is nastier than CoF in some ways. no error shape, flow just stops, everything upstream shows green. i add explicit item count checks on nodes where empty output is a real failure, not just a quiet edge case.

on setup: central Error Trigger for obvious failures, but that leaves the CoF and zero-items cases uncovered. been building something specifically for the green-but-broken pattern. what’s the most common place you see zero items bite people in production?

Hit this exact thing in production with client-facing report workflows. The inline IF-after-CoF pattern works, but it has one structural weakness: every check lives inside the workflow it’s checking. If the flow stops early (your zero-items case), or someone edits a node and drops the check branch, the validation dies with it — and everything upstream still shows green.

What I converged on: keep the inline checks for routing, but move health validation OUTSIDE the workflow. Each production workflow writes one line of run metadata (status, item counts, timestamp) to a log sheet as its last step; a separate scheduled watcher reads the log and alerts on missing runs, error rows, or zero-count outputs. The watcher survives anything that happens inside the workflows it monitors — including them not running at all, which no inline check can ever catch.

Curious at what scale this starts hurting for people — how many production workflows are you running when you built your CoF discipline?

@dima_automation For zero items biting in production - the most common place I see it is Google Sheets “Get Rows” with a filter that returns nothing (expired token, sheet renamed, wrong range). The workflow executes cleanly with zero items, every downstream node produces zero items, and the final send/write node just never runs. No error, green log, client gets no report. Second most common: HTTP Request node pagination that exits early because the API changes the response schema and the “has more pages” check returns false. The fix I use: after any node where zero output would be a real failure, add an IF node checking {{ $items().length === 0 }} and route that to an explicit alert rather than continuing silently.

Both of those are textbook, especially the Sheets Get Rows with a filter that quietly matches nothing, downstream all zero, final send never runs, green log. That is the cleanest write-up of the whole problem I have seen.

The IF node checking {{ $items().length === 0 }} is the right in-flow guard where zero is unambiguously a failure. One thing worth flagging: it catches your first case but not your second. The early-pagination exit returns a truncated but non-zero set, so length === 0 sails right past it, the flow got 40 of 200 rows and every node is happily non-empty. That one needs an expected-count or a baseline check, roughly is this far below what this run normally produces, not just zero versus not.

And both patterns still assume the run fired. The nastiest version is the scheduled workflow that never triggered at all, no IF node runs because nothing ran, nothing reads as zero because nothing executed. That is the case that pushed me toward checking the outcome from outside the run, a per-node guard cannot guard a run that did not happen. Your zero-check plus a count baseline plus an outside liveness check covers most of the surface between them.