data/skills/n8n-error-handling/README.md
Wire n8n error handling so failures are loud, structured, and recoverable — instead of the default, where a single node throwing halts the whole workflow and the caller or operator gets nothing.
When an n8n node throws, the workflow stops. For a run you're watching that's fine — you see the red node. For anything unattended it's the wrong default:
| Workflow | What the default does | What you wanted |
|---|---|---|
| Webhook / API | Caller gets a timeout or a bare 500 | A 4xx/5xx with a body that says what broke |
| Scheduled / cron | Job stops; nobody is told | An alert the moment it fails |
| Agent tool / queue worker | Silently drops work | A handled, recoverable failure |
This skill turns silence into a routed, structured, recoverable failure.
onError: "continueErrorOutput" and wiring main[1]. One without the other is the #1 silent trap.retryOnFail on network nodes so transient blips never reach an error pathonError set but error output unwired → run shows succeeded while dropping workonError unset → handler unreachable, workflow haltsinternal_error for everything, including bad inputActivates when you:
onError, continueErrorOutput, error branch, main[1])retryOnFail, maxTries, waitBetweenTries)Example queries:
Main skill content — loaded when the skill activates.
retryOnFail self-healing before wiring error pathsThe two-step setup on a single node, in depth.
onError values) and step 2 (addConnection with sourceIndex: 1)n8n_get_workflow verificationThe webhook → Respond pattern under failure.
Response body and status-code conventions.
responseCode defaults to 200 — set it on every error branchThe workflow-level catch-all.
// 1) create the error output
{ type: "updateNode", nodeName: "HTTP Request",
changes: { onError: "continueErrorOutput" } }
// 2) wire it (sourceIndex 1 = error output)
{ type: "addConnection", source: "HTTP Request", target: "Handle Error", sourceIndex: 1 }
{ type: "updateNode", nodeName: "HTTP Request",
changes: { retryOnFail: true, maxTries: 3, waitBetweenTries: 5000 } }
{ "responseCode": 502,
"responseBody": "={{ JSON.stringify({ error: 'upstream_error', message: 'External service failed' }) }}" }
onError values"stopWorkflow" (default) — halt the workflow"continueErrorOutput" — route the error out main[1] (the one you wire)"continueRegularOutput" — error flows out the normal output (rare, usually wrong)n8n-workflow-patterns: the webhook/API and scheduled patterns are where error handling lives — use it for the shape, this skill to harden it.
n8n-node-configuration: onError/retryOnFail are node config; NODE_FAMILY_GOTCHAS.md covers the Webhook/Respond response-code traps in detail.
n8n-validation-expert: a half-wired error output is a connection/config audit item, not a validation error — this skill is the fix.
n8n-expression-syntax: the expression-driven Response Code and alert messages depend on correct {{ }} and $json.error access.
n8n-code-javascript / n8n-code-python: if you catch errors inside a Code node, decide deliberately — re-throw to use the error output, or handle and continue; don't return error-shaped data as success.
n8n-binary-and-data: file/binary operations are fallible too — wire their error outputs like any network node.
| Situation | Posture |
|---|---|
| Anyone but you sees the output (downstream system, end user, on-call) | Full handling — the rules above apply |
| Internal one-off you run and watch yourself | onError: "stopWorkflow" is fine — you'll see it and re-run |
After using this skill, you should be able to:
onError and main[1], verified via n8n_get_workflowretryOnFail so transient failures self-heal before reaching an error pathAuthoritative facts in this skill come from:
onError semantics and the main[1] error output contractmaxTries ≤ 5, waitBetweenTries ≤ 5000ms; retries on any error)execution/workflow/error)n8n_update_partial_workflow operations (updateNode, patchNodeField, addConnection, activateWorkflow)Version: 1.0.0
Compatibility: n8n with per-node onError outputs; community n8n-mcp server for all workflow operations.
Part of the n8n-skills project.
Remember: the default is silence. Make the failure route (per-node onError + wired output, or a catch-all error workflow) and make it speak (a truthful status code and body). Half a move is worse than none — it looks done.