I linted the 400 most-viewed n8n templates: 3% of HTTP nodes retry, 1% have a timeout, 2 of 67 webhooks are authenticated

I run n8n for client work and the failure that costs me the most is never the red execution. It is the green one that did nothing: a token expired, a field got renamed upstream, a filter stopped matching, and every node is green with zero rows written. Several threads here in the last month are about exactly that, so I wrote a small static checker for the things you can see in the workflow JSON before it ever runs, and then pointed it at the template library to see how common the gaps are.

The 400 most-viewed public templates (pulled through the public template API this week):

HTTP Request nodes 628. 20 have Retry On Fail (3%). 8 have a timeout (1%).
Webhook triggers 67. 2 use any authentication.
Templates that include settings 205. 142 (69%) have no error workflow set.
AI Agent nodes 248. In 67 templates the agent’s output goes straight to the next node with no check and no error branch.
IF / Switch with an unconnected branch 22% of templates. Items that take that branch just vanish.
Nodes on Continue On Fail with nothing downstream looking at the error 8% of templates.

Templates are starting points, not production, so none of that is a dig at the authors. It is a measure of how much hardening sits between “works in the editor” and “runs for a client for a year”.

The checker is n8n-workflow-lint. No install, no signup, nothing leaves your machine:

npx github:tachyurgy/n8n-workflow-lint my-workflow.json

It prints a score out of 100 and a list like this:

ERROR no-error-workflow        settings.errorWorkflow is not set
ERROR agent-unguarded  @ "Qualify lead"   agent has 3 tool(s) and its output goes straight to "Update CRM"
WARN  http-no-retry    @ "Enrich company"  retryOnFail is off
WARN  dead-end-branch  @ "Is qualified?"   false branch is not connected
INFO  unasserted-write @ "Update CRM"      terminal write node; nothing checks what it did

15 rules. Each one has a why, a fix, and a link to the thread on this forum where somebody lost days to it (--rules prints them). It also takes --min-score 75 so it can sit in CI if you keep exported workflows in git.

Two honest limits. It is static, so it can tell you where a workflow can fail quietly, not that it is; the runtime half is a separate set of importable workflows I will post in Built with n8n. And a couple of the rules are opinions (a workflow ending on a write with no post-condition is an info, not a crime); --ignore exists.

Repo: GitHub - tachyurgy/n8n-workflow-lint: Static production-readiness checks for n8n workflow JSON: silent-failure traps, missing retries/timeouts, swallowed errors, unguarded AI agents, leaked secrets. · GitHub
Write-up with the method: The green run that did nothing — open-source n8n reliability tooling | Levelbrook

If it fires on something you did on purpose, I would like the export. The goal is that every finding is something a senior engineer would actually flag in review.

Useful numbers, and the 3% is lower than I would have guessed.

One thing about http-no-retry though. I read the rule, it fires on every HTTP Request node with retryOnFail off, and the fix text says turn it on with 3 tries. For a GET that is right. For a POST to anything that charges per call it can be the wrong advice: Retry On Fail cannot tell “the request never arrived” from “it arrived and only the response got lost”, so the second case runs the job twice and bills twice.

So the missing retry and the unsafe retry are two different findings. A rule I would add:

  • retryOnFail is on, the method is POST, PUT or PATCH, and there is no Idempotency-Key header in parameters.headerParameters: warn, “retry on a non-idempotent call”
  • and http-no-retry stays a warn for GET, but becomes an info for the write methods, with the fix text pointing at the key first

The key that works in n8n is {{ $execution.id }}-{{ $itemIndex }}. With the execution id alone every item in the run shares one key, I wrote that up here this morning: Retry On Fail + paid API = paid twice (Idempotency-Key)

Your static check could even verify that part, an Idempotency-Key expression that contains $execution.id but not $itemIndex is almost certainly a bug.

These are the numbers I did not have, thank you. Mine from the other side: 27 workflow branches skipped for weeks with every run recorded COMPLETED, because a skipped step had no enum value to write, so nothing counted it. And a stored tool result that read ok: true next to status 403, which is the same bug one layer down: the call completed, the answer was a refusal. The methodology question I cannot answer from your post: the template library is mostly demos, so how much of the 69% with no error workflow is production practice rather than nobody bothering on a sample? If you ran the same linter over workflows people actually run for clients, I would expect the retry number to move and the error-workflow number to move a lot.

Fair point, and you’re right that those are two different findings. A retry on a POST with no key is worse than no retry at all if the endpoint bills per call.

Splitting it the way you describe (warn on retry plus a write method with no Idempotency-Key, and drop http-no-retry to info for writes) makes more sense than what the rule does now. The $execution.id without $itemIndex check is a good catch too, I hadn’t thought of that one.

Honest answer: I can’t separate those from this sample. The library is mostly demos, so the 69% says more about templates than about production, and I’d read it as a ceiling rather than a production rate.

The 27 skipped branches logged as COMPLETED is a nasty one. Thanks for sharing it.

That is the honest answer, and it makes the number more useful rather than less. A ceiling bounds the claim, which is more than most posts here manage. The production rate would need the linter to run where the workflows actually run, so the only route I can see is handing it to people and asking them to report the four counts back. I cannot give you a sample myself, mine is not n8n.