Skip to content

/n8n-error-handling

Wire n8n error handling so failures are loud, structured, and recoverable. Use when building any webhook/API workflow, a scheduled or unattended workflow, or any path where a silent failure would drop user-visible work — and whenever the user mentions error handling, onError,

From plugin
n8n-mcp-skills
6.6k15 skills3 hooks

Claude Code

$ npx -y skills add czlonkowski/n8n-skills --skill n8n-error-handling --agent claude-code

SKILL.md

n8n-error-handling.SKILL.md
name: n8n-error-handling
description: Wire n8n error handling so failures are loud, structured, and recoverable. Use when building any webhook/API workflow, a scheduled or unattended workflow, or any path where a silent failure would drop user-visible work — and whenever the user mentions error handling, onError, continueErrorOutput, error branches/outputs, retries, retryOnFail, Respond to Webhook status codes, 4xx/5xx, Error Trigger, or "my workflow fails silently". Covers per-node error outputs and wiring, retry/self-healing, error-trigger workflows, and 4xx/5xx response shapes.

n8n Error Handling

By default, when an n8n node throws, the **whole workflow halts**. For an interactive run you're watching, that's fine — you see the red node and fix it. For anything unattended (a webhook API, a cron job, a queue worker, an agent tool), it's the wrong default: the caller gets a timeout or an empty 500, the operator gets no alert, and the symptom is "the integration just stopped working" with no log and no clue.

This skill is about making failures **loud, structured, and recoverable** — and, best case, **self-healing** so transient blips never reach a human at all.

The two ideas that prevent most silent failures:

  • **Per-node error outputs** — a node's failure routes down a second output you control, instead of killing the run.
  • **A workflow-level error workflow** — a catch-all that fires for anything that escapes per-node handling (timeouts, crashes between nodes, unwired failures).

---

When you actually need this

| Workflow shape | Error handling posture | |---|---| | Webhook / API (anything with `Respond to Webhook`) | **Required.** Every fallible node's error output wired; status code matches cause. | | Scheduled / cron / queue worker / agent tool (unattended) | **Required.** A workflow-level error workflow, plus `retryOnFail` on network nodes. | | Internal one-off you run and watch yourself | **Optional.** Default `onError: "stopWorkflow"` is fine — you'll see the red node and re-run. |

The dividing line: **if anyone other than you sees the output** — a downstream system, an end user, an on-call engineer — the failure has to be handled, not swallowed. If you're the only watcher and the cost of failure is "I notice and re-run", looser is fine.

---

The #1 silent trap: per-node error output is a TWO-step setup

This is the single most common way an n8n workflow "handles" errors while actually swallowing them. Routing a node's failure to a handler takes **two** changes, and doing only one looks complete but misbehaves:

1. **Set `onError: "continueErrorOutput"`** on the node. This is what *creates* the second output. Without it, `main[1]` doesn't exist no matter what you wire. 2. **Wire that error output** (`connections.<node>.main[1]`, i.e. `sourceIndex: 1`) to a real handler. Without a target, the error data is emitted into the void.

Get one without the other and you hit a failure mode:

| What you did | What happens at runtime | |---|---| | `onError` set, error output **not** wired | Error data is silently discarded. Downstream doesn't fire. The dashboard shows the run as **succeeded**. Worst case — no error logged anywhere. | | Error output wired, `onError` **not** set | The slot never fires; the handler is unreachable. On failure the workflow just **halts** (default `stopWorkflow`). | | Both done | Failure routes down `main[1]` to your handler. ✅ |

Doing both with `n8n_update_partial_workflow`

// 1) Turn on the error output (creates main[1])
{ type: "updateNode", nodeName: "HTTP Request",
  changes: { onError: "continueErrorOutput" } }

// 2) Wire the error output to a handler. sourceIndex: 1 = the error output.
{ type: "addConnection",
  source: "HTTP Request",
  target: "Handle Error",
  sourceIndex: 1 }

`sourceIndex: 0` is the success path, `sourceIndex: 1` is the error path. (For IF nodes the aliases `branch: "true"`/`"false"` map to index 0/1; for a generic fallible node, use the explicit `sourceIndex: 1`.)

**Then verify.** This trap doesn't surface in `validate_workflow` — a half-wired error output validates clean. Pull the workflow with `n8n_get_workflow` and confirm **both** halves:

  • The node's `onError` is `"continueErrorOutput"`.
  • `connections["HTTP Request"].main[1]` contains your handler.

Valid `onError` values:

| Value | Effect | |---|---| | `"stopWorkflow"` (default) | Error halts the whole workflow. | | `"continueRegularOutput"` | Error item flows out the **normal** output. Rare, usually wrong — downstream gets error-shaped data and keeps going. | | `"continueErrorOutput"` | Error item flows out the **separate** error output (`main[1]`). The one you wire. |

Full failure-mode catalog, fan-in/fan-out shapes, and verification: **NODE_ERROR_OUTPUTS.md**.

Failures the error output never sees

A correctly wired error output still misses whole classes of failure. Verified on n8n 2.38.5:

| Failure | With `continueErrorOutput` | With `continueRegularOutput` | |---|---|---| | JS error **inside `{{ }}`** (TypeError on a missing path, `JSON.parse` on bad input, a thrown `Error`, a JMESPath syntax error) | nothing fires; the field is `null` and items take the **success** path | same | | Python Code node **rejected before running** (blocked import, dunder access: `Security violations detected`) or **bad return shape** (list in each-item mode) | node marked failed, but the **unchanged input items leave through the success output**; `main[1]` stays empty and the execution shows success | unchanged input items pass through | | Exception **while** code runs (`raise`/`throw`, `KeyError`, `NameError`) | routed to `main[1]` as `{ error }` ✅ | `{ error }` item on the main output |

So an error branch alone can't guard these:

  • **Guard the data, not just the node.** After a transform whose output matters, check that the

field you produced exists (IF/Filter on `{{ $json.total !== undefined && $json.total !== null }}`) and se

Read more
@czlonkowski
Ships withn8n-mcp-skills

Expert Claude Code skills for building flawless n8n workflows using the n8n-mcp MCP server

Stars on GitHub

Momentum

stars in total
6.6k
Star history of czlonkowski/n8n-skills on GitHub, a chart by star-history.com

Other skills on n8n-mcp-skills.

n8n-agents
Auto-invoked

n8n-agents

Design n8n AI agents the right way. Use when building or editing any @n8n/n8n-nodes-langchain.* AI node — an AI Agent, LLM chain,…

n8n-code-python
Auto-invoked

n8n-code-python

Write Python in n8n Code nodes (native Python, `language` pythonNative, n8n 2.x). Use when the user explicitly wants Python in a…

n8n-code-tool
Auto-invoked

n8n-code-tool

Write JavaScript or Python for the n8n Custom Code Tool (@n8n/n8n-nodes-langchain.toolCode) — the AI-agent-callable tool, NOT the…

More skills from other plugins.

agent-builder

agent-builder

Load immediately after an Agent intent, before the first agent_builder_* call. Governs how you create, configure, test, and…

n8n11d209k
@n8n-io@n8n-ioInstall Skill
browser-use

browser-use

Direct browser control via CDP for web interaction: automation, scraping, testing, screenshots, and site/app work. Also covers…

@browser-use@browser-useInstall Skill
agent-browser

agent-browser

Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling…

@vercel-labs-2@vercel-labs-2Install Skill
add-anydoc

add-anydoc

Add local office-document-to-Markdown conversion to NanoClaw agent containers with the pinned Firecrawl AnyDoc CLI. Use when…

@nanocoai@nanocoaiInstall Skill

Questions about n8n-error-handling.

Which coding agents can use n8n-error-handling?
n8n-error-handling installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.