I wrote a free linter for n8n workflows: 12 checks for the failures that cost money

I wrote a free linter for n8n workflows: 12 checks for the failures that cost money

Most n8n reviews look at the happy path. The expensive failures are elsewhere: the run that dies quietly, the message that gets marked read and never recorded, the retry that sends twice, the handover note that promises threading the send node cannot do.

I turned the checks I use when auditing workflows into a small script. It reads an exported workflow JSON and reports what it finds. No instance access, no credentials, no network, no dependencies.

What it checks

Severity Rule What it catches
high no-error-workflow No error workflow set - failures are silent
high imap-read-before-log Mail marked read before it is recorded
high confidence-defaults-high A missing confidence value treated as certain
high threading-claim-false Docs promise In-Reply-To that the node cannot set
high autosend-hardcoded-on Auto-send hardcoded, human gate gone
medium http-no-retry A transient 502 kills the whole run
medium webhook-open Webhook accepts unauthenticated requests
medium no-idempotency A retried trigger is processed twice
medium no-send-log Sends mail, records nothing
medium empty-draft-sendable An empty draft can reach the send step
low trigger-doc-mismatch Node name contradicts its schedule
low http-no-timeout Hung HTTP call blocks the queue

What it found in my own template

I ran it against my own published workflow (Triage inbound email with IMAP, custom AI HTTP APIs and SMTP | n8n workflow template). Four high findings, including the one I am least proud of: the sticky notes claimed In-Reply-To / References were set, and the send node had an empty options object. n8n’s Send Email node does not support those headers at all - every reply opens a new conversation. My documentation was describing something impossible, which is worse than simply not threading.

That is the point of the tool: it found the lie in my own handover note before a customer did.

Usage

python n8nwf_lint.py workflow.json
python n8nwf_lint.py workflow.json --json          # for CI
python n8nwf_lint.py workflow.json --min-severity high

Exit code is 1 when findings meet the threshold, so it works as a CI gate unchanged.

Repo: GitHub - Miss567/n8nwf-lint: Static reliability checks for exported n8n workflows - silent failures, missing retries, false threading claims. No dependencies, MIT. · GitHub - MIT, Python 3.8+, no dependencies.

Two honest notes. It is a static heuristic check on the graph, not a behavioural test - it cannot see runtime data, so it will miss things and may flag things you consider fine. If a rule is wrong, tell me and I will fix it; that is more valuable to me than a star.

And yes, I do this for a living: a fuller audit is USD 45, and fixes are quoted before starting. The tool being free is not a trick - if the report is enough for you to fix it yourself, that is a good outcome.

Sample output: the screenshot below is the real report on my template.

If you want the client-facing part: the free script prints findings; the paid kit turns the same checks into a branded HTML/PDF audit report you can hand to a client (one command, your name and the client’s name on it): https://payhip.com/b/ZHoiv - USD 19.

This is a useful approach to n8n reliability. I especially like the checks for idempotency, missing timeouts, unauthenticated webhooks, and send logging. Static linting is a great first gate, though I’d pair it with runtime tests for retries, duplicate events, and failure recovery before calling a workflow production-ready. Thanks for sharing this!

Thanks — and your list is the right gap. Static checks can see the graph; they cannot see what happens when the 429 arrives at 14:03 on a Friday.

On the runtime side, the three I would test first are the same three that keep showing up in real incidents:

  1. Retry idempotency. Force the HTTP step to fail after the write, let the retry run, and check whether you now have two rows. That is the check nobody writes because it needs a deliberate half-failure.
  2. Duplicate trigger delivery. Deliver the same webhook twice with the same message id and see whether anything downstream acts twice. Most workflows pass this by accident because the provider de-duplicates, not because the workflow does.
  3. Recovery after a dead step. Kill the workflow mid-run (disable a node), let a few messages queue, re-enable it, and see whether the missed ones come back. This is where “mark as read on fetch” quietly loses mail.

If you want a starting point beyond words: the static ones are a free Python script (repo in the post), and I packaged the client-facing half — the HTML/PDF report generator, plus two smaller tools for version diffing and turning an export into a diagram — as a paid kit, because agencies kept asking for the deliverable rather than the findings. Link is in the repo README if it is useful; the free checker stays free either way.

What would you add to the runtime list? I would rather the checklist be wrong here than in a client handover.

Small update, since a few people asked what the paid side actually is. All three are live now, one-time prices, no subscription:

  • Workflow reliability audit kit, USD 19 - the same 12 checks, plus a client-ready HTML/PDF report with your name and the client’s name on it: https://payhip.com/b/ZHoiv
  • Workflow diff, USD 12 - compares two exports and tells you what actually changed (nodes, parameters, retry flags, connections), because “what did you change?” deserves a diff rather than a memory: https://payhip.com/b/6dHMA
  • Workflow diagram, USD 12 - turns an export into a Mermaid diagram and a handover page, so the client sees the flow instead of JSON: https://payhip.com/b/LydlD

The free checker is unchanged and stays free: GitHub - Miss567/n8nwf-lint: Static reliability checks for exported n8n workflows - silent failures, missing retries, false threading claims. No dependencies, MIT. · GitHub

If a check in the free tool is wrong for your workflow, tell me here and I will fix the rule - that is worth more to me than a sale.