DEV Community

Crzy undefined (undefined)
Crzy undefined (undefined)

Posted on

I Import-Tested 10 of My Own n8n Workflows on a Fresh Install. 3 Failed

I've been publishing n8n workflows for a few weeks now. Every one of them worked on my n8n. That's the problem: my n8n has credentials I created months ago, URLs I pasted in and forgot about, and a version of me who already knows which node needs editing.

So I did the boring thing I should have done before publishing anything. I took 10 of my own workflows, spun up a completely fresh n8n, and set each one up using only the instructions I actually ship with it. No memory, no shortcuts.

Short version: all 10 imported cleanly and all 10 activated. 3 of the 10 still failed to do their job, and the two worst failures showed up as green, successful executions.

The setup

  • n8n 1.123.84, installed from npm, running on Node 24, with a brand-new, empty user folder. No credentials, no other workflows, nothing carried over.
  • The 10 workflows: a lead qualifier that pings Slack, a support FAQ agent, a cold email personalizer, an invoice extractor, a daily market digest from RSS to Slack, meeting notes to action items, a review-reply agent, a content repurposer, a resume screener, and a weekly metrics narrator that posts to Slack. Two of them (the lead qualifier and the review-reply agent) are the ones in my free repo. The files I tested are byte-for-byte the same as the repo copies.
  • The model: a small mock OpenAI-compatible server on localhost that returns valid JSON in the shape each workflow asks for. This post is about whether the workflows survive a fresh install, not whether a model gives good answers, so I took the model out of the equation. Slack and the metrics endpoint were mocked locally too.

All 10 have the same basic shape: a trigger (webhook or schedule) → a Config Code node → a Build Prompt Code node → an HTTP Request to /chat/completions → a Parse Result Code node → respond or post somewhere.

The rule I set before running anything

A workflow passes if, after doing only the documented setup steps, one realistic run finishes and does its job: returns the result to the caller, or posts the message to Slack.

"Documented setup" meant exactly two sources:

  1. The repo README's quick start: create a Header Auth credential (Authorization / Bearer YOUR_KEY), select it on the LLM node, and set baseUrl and model in Config.
  2. Whatever the workflow's own Read Me sticky note tells you to do.

If a step wasn't written down, I didn't do it. That's what a new user would do.

Stage 1: is the JSON even valid?

I wrote a small script that parses each file and checks every node's type and typeVersion against the node list that ships with n8n 1.123.84.

Check Result
Valid JSON 10 / 10
Every node type exists 10 / 10
Every typeVersion is still supported 10 / 10
Running the latest version of every node 0 / 10

None of them use the newest node versions. The Webhook nodes are v2 (latest is 2.1), HTTP Request is 4.2 (4.3), Respond to Webhook is 1.1 (1.5), and RSS Feed Read is v1 (1.2). n8n keeps old node versions working, so this isn't a failure, and I was a bit disappointed that my "deprecated node" theory didn't pan out.

The other thing the script flagged: none of the files have a workflow id. That turned out to matter later.

Stage 2: import

I imported each file one at a time with n8n import:workflow.

10 / 10 imported. No errors, no warnings about my files.

Stage 3: activate before setting anything up

Here's where it got interesting. Before creating any credential, I tried activating all 10 workflows exactly as imported. Every LLM node still points at a credential with the id REPLACE_ME, which doesn't exist on this install.

10 / 10 activated. n8n happily turned all of them on.

Then I called each of the 8 webhook workflows once with a realistic payload:

What the caller saw What the execution log saw
HTTP 200, empty body, in about 2 seconds ❌ Error at the LLM node: Credential with ID "REPLACE_ME" does not exist for type "httpHeaderAuth".

All 8 behaved the same way. If you've got a form, a Zap or a script calling one of these webhooks, it gets a 200 and moves on. Unless you check for an empty body, nothing on the calling side looks wrong.

To be clear, that's a "you didn't set it up yet" situation, so I don't count it as a failure. But it's a good reminder that "active" in n8n means "the trigger is listening", not "this workflow works".

Stage 4: the documented setup

I created one Header Auth credential, selected it on every LLM node, and pointed baseUrl / model in each Config node at the mock server. Then I did what each sticky note said:

  • Daily market digest: "Set Header Auth credential on LLM; paste Slack webhook URL." So I pasted a Slack webhook URL (the local mock).
  • Weekly metrics narrator: "Point Fetch Metrics at any JSON." So I pointed it at a local metrics JSON.
  • Everything else: the sticky only covers the credential and Config, which I'd already done.

Stage 5: run each one once

# Workflow Execution Did its job? Result
01 Lead qualifier → Slack ✅ success ❌ no Slack alert FAIL
02 Support FAQ agent ✅ success ✅ pass
03 Cold email personalizer ✅ success ✅ pass
04 Invoice extractor ✅ success ✅ pass
05 Daily market digest → Slack ❌ error at Read RSS ❌ FAIL
06 Meeting notes → actions ✅ success ✅ pass
07 Review-reply agent ✅ success ✅ pass
08 Content repurposer ✅ success ✅ pass
09 Resume screener ✅ success ✅ pass
10 Weekly metrics → Slack ✅ success ❌ no Slack post FAIL

7 passed, 3 failed. The pattern was obvious: every workflow that only talks back to its own caller passed. All 3 that reach out to another service failed.

Here's each failure.

Failure 1: the lead qualifier "posted" to Slack and nothing arrived

The webhook returned exactly what it should:

{"score": 82, "tier": "hot", "summary": "Series A SaaS, 40 staff, budget approved, wants a demo this week", "reasons": ["ICP fit", "budget stated", "timeline < 30 days"], "next_step": "Book a demo"}
Enter fullscreen mode Exit fullscreen mode

Score 82 is above the 70 threshold, so the Hot Lead? IF node sent it down the true branch to Post to Slack. The execution went green and the mock Slack got nothing.

The reason: Post to Slack still had my placeholder URL in it:

https://hooks.slack.com/services/REPLACE/WITH/YOUR_WEBHOOK
Enter fullscreen mode Exit fullscreen mode

You'd expect that to fail loudly. It doesn't. Slack answers that URL with a 302 redirect to its developer docs page. The HTTP Request node follows the redirect by default, gets a 200 with a page of HTML, and records it as a success. The node output is literally the Slack docs homepage:

{"data": "<!doctype html><html lang=en dir=ltr class=\"docs-wrapper plugin-docs ...\"><title data-rh=true>Slack developer docs | Slack Developer Docs</title>..."}
Enter fullscreen mode Exit fullscreen mode

And the sticky note never says to change the Slack URL. It tells you about the credential and Config, then stops. So a new user does everything the instructions say, sees green executions, and never gets a single hot-lead alert. That's the worst kind of failure, because there's nothing telling you to go and look.

Failure 2: the weekly metrics narrator, same bug

Same placeholder, same redirect, same green execution, no Slack message. Its sticky note only mentions pointing Fetch Metrics at your JSON. It doesn't mention the Slack URL, and it doesn't mention the LLM credential either (I only knew to set that from the repo README).

I'd written the market digest's note properly ("paste Slack webhook URL") and then forgot to write the same line on two other workflows. I'm fairly sure that's how most setup bugs happen: the instructions get written for the workflow you built first.

Failure 3: the market digest couldn't read its feed

This one failed loudly, at the very first node after the trigger:

Read RSS: socket hang up
Enter fullscreen mode Exit fullscreen mode

The default feed is https://news.ycombinator.com/rss. From my test server, that host never finishes a TLS handshake. curl fails the same way, while hnrss.org, TechCrunch's feed and the dev.to feed all return 200 from the same machine. So this one is partly about my environment rather than the JSON itself.

I'm still counting it, because it's exactly what a fresh install in a new environment looks like. A cloud VM, a corporate network or a home server can each have their own idea of which hosts they can reach. The workflow depends on one hardcoded feed with no fallback, so when that one host is unreachable, the whole digest just doesn't happen. (At least this one shows up as a red execution.)

Checking that those really were the only problems

To make sure I hadn't missed anything else, I changed exactly one thing in each failed workflow and ran it again:

Workflow One change Result
01 Lead qualifier real Slack URL in Post to Slack ✅ success, Slack alert arrived
05 Market digest feed → https://hnrss.org/frontpage ✅ success, digest posted
10 Metrics narrator real Slack URL in Post to Slack ✅ success, summary posted

So none of the three were broken JSON. They were broken setup instructions plus a single point of failure.

Bonus failure: importing the same file twice

The missing workflow id from stage 1 shows up here. I imported the review-reply workflow a second time, like you might after tweaking the file:

  • n8n created a second copy instead of updating the first one (no id, so nothing to match on).
  • Activating the second copy failed with: "The URL path that the "Webhook" node uses is already taken. Please change it to something else."

That's a reasonable error, and at least it's loud. But if you deactivate the old copy and activate the new one, you can end up with two near-identical workflows and no clear sign of which one is serving your webhook.

The fix I'm using now: make Config refuse to run until setup is done

The real problem behind failures 1 and 2 is that the values a user has to edit are scattered across different nodes, and the placeholders don't fail. So I moved every "you must change this" value into the Config node and made it throw if any of them still look like a placeholder:

// Edit these values. Any OpenAI-compatible endpoint works (OpenAI, Groq, OpenRouter, Together, local Ollama /v1).
const cfg = {
  baseUrl: 'https://api.openai.com/v1',
  model: 'gpt-4o-mini',
  slackWebhook: 'https://hooks.slack.com/services/REPLACE/WITH/YOUR_WEBHOOK',
};
// Fail loudly if setup isn't finished, instead of "succeeding" against a placeholder.
const unset = Object.entries(cfg)
  .filter(([, v]) => !v || /REPLACE|YOUR_|example\.com/i.test(v))
  .map(([k]) => k);
if (unset.length) throw new Error(`Setup not finished: set ${unset.join(', ')} in the Config node`);
return [{ json: { ...$input.first().json, ...cfg }, pairedItem: 0 }];
Enter fullscreen mode Exit fullscreen mode

Then Post to Slack reads its URL from Config instead of hardcoding it:

{{ $node['Config'].json.slackWebhook }}
Enter fullscreen mode Exit fullscreen mode

A small gotcha I hit here: on this n8n version, {{ $('Config').first().json.slackWebhook }} in the HTTP Request URL field came back as undefined ("URL parameter must be a string, got undefined"), even though Config's output clearly had the value. $node['Config'].json.slackWebhook worked. I didn't dig into why. If you hit the same error, try the other syntax.

I tested the fixed lead qualifier on the same fresh install, twice:

Slack URL in Config Execution Slack
placeholder left in ❌ error at Config: Setup not finished: set slackWebhook in the Config node nothing sent
real URL ✅ success alert arrived

Now the forgotten step turns into a red execution with an error message that tells you exactly what to fix. It also means an Error Trigger workflow (the setup from my earlier post on silent failures) will catch it.

The caller still gets an HTTP 200 with an empty body when the workflow errors before Respond to Webhook, like in stage 3. If your caller matters, treat an empty body as a failure on that side too.

What I'm changing in how I ship workflows

  1. Every value a user must change lives in one Config node, and Config throws if any of them still contain REPLACE, YOUR_ or example.com.
  2. The sticky note lists every required step, generated from that Config list, not written from memory.
  3. External calls get checked, not just executed. A 200 from a webhook URL that redirected to a docs page isn't a success. For Slack specifically, a real incoming webhook replies with ok, so checking for that catches the redirect case.
  4. Default feeds and URLs should be ones that work almost everywhere, or come with a fallback. A digest that depends on one host is a digest that silently doesn't arrive one morning.
  5. Ship workflow files with a stable id, so re-importing updates the workflow instead of creating a copy.
  6. Run this test before publishing. Fresh user folder, import, follow only the written steps, run once. It took me under an hour for 10 workflows, and it found problems I'd shipped weeks ago.

The free workflows

The lead qualifier and the review-reply agent are in the free repo: arkintof/n8n-ai-agent-workflows, under workflows/. If you're using the lead qualifier from there, paste your Slack webhook URL into Post to Slack, because its Read Me doesn't tell you to. Or replace its Config node with the guard above and point Post to Slack at {{ $node['Config'].json.slackWebhook }}.

When was the last time you imported one of your own workflows into a clean n8n and followed only your own instructions? And how do you catch a "success" that didn't actually do anything?

Written with AI assistance; workflow built and tested by me.

Top comments (0)