Crash Labs
n8n stress-test report
Deploy gate
FAIL
Not production-ready yet
"Invoice on payment webhook" should not go live yet: 7 critical issue(s) (score 100/100). Biggest risk: "Create Invoice" can fail with no backup path (Create Invoice).
Risk score
100/100
- Nodes
- 4
- Critical
- 7
- High
- 6
- Outbound
- 3
- Coverage
- 100%
Coverage 100%. Outbound paths 100% simulated.
How to fix (do these first)
- "Create Invoice" can fail with no backup path — Turn on retry for transient SMTP/API errors, and route hard failures to an error branch that alerts someone.
- "Create Invoice" retries can create duplicates — Before create/send: check "already processed?" using a unique event/message id. Prefer upserts over creates.
- Webhook "Payment Webhook" can fire side effects twice — Store the provider event id; if you've seen it, skip every create/send node.
Key takeaways
- If production retried this 50×, Northwind Ops (simulated) would send 47 copycat emails and create 47 Stripe objects for the same customers.
- Coverage 100%. Outbound paths 100% simulated.
- Mock simulation ran 4 steps · 3 external action(s) on happy path.
- Storyboard sample: Run #1: 1 invoice + 1 email + 1 message · Run #2: 1 invoice + 1 email + 1 message · Run #3: 1 invoice + 1 email + 1 message
- Silent quota drain: Email / ESP 100 calls (soft 5); Payments 100 calls (soft 3); Team chat 100 calls (soft 8) — 2800 budget unit(s) burned with no hard stop.
- Same event twice: 3 outbound actions ran again on the retry.
- Simulation scenarios: 0/8 passed (N/A counts as pass).
- Top issue: "Create Invoice" can fail with no backup path @ Create Invoice
Outbound actions
- Payment / billingCreate Invoice
Talks to Stripe to create a invoice. This can move money or create invoices.
- Customer emailEmail Customer
Sends an email to a value from the data ("customerEmail") - set when the workflow runs. Subject: Your invoice. This is a real message leaving your system - not a draft inside n8n.
- Team messageNotify Slack
Posts a message to Slack (#ops). Your team (or a channel) will see it.
Blast radius · Northwind Ops (simulated)
If production retried this 50×, Northwind Ops (simulated) would send 47 copycat emails and create 47 Stripe objects for the same customers.
Across 50 simulated runs: 50 near-identical chat messages posted.
- Emails
- 50
- SMS
- 0
- Invoices
- 50
- Stripe
- 50
- Commerce
- 0
- CRM
- 0
- Files
- 0
- Customers
- 3
Run storyboard
- Run #1: 1 invoice + 1 email + 1 message
- Run #2: 1 invoice + 1 email + 1 message
- Run #3: 1 invoice + 1 email + 1 message
- …
- Run #14: 1 invoice + 1 email + 1 message
- …
- Run #25: 1 invoice + 1 email + 1 message
- …
- Run #48: 1 invoice + 1 email + 1 message
- Run #49: 1 invoice + 1 email + 1 message
- Run #50: 1 invoice + 1 email + 1 message
- CRITICAL: 50 near-identical chat messages posted.
- CRITICAL: 47 copies of the same email went to ada@example.com (subject: "Your invoice").
- CRITICAL: 47 Stripe stripe:invoice(s) for customer key "ada@example.com".
- HIGH: 2 copies of the same email went to cara@example.com (subject: "Your invoice").
- HIGH: 2 Stripe stripe:invoice(s) for customer key "cara@example.com".
Retry survival · same event twice
Second delivery still sent 1 email(s) and ran 1 payment action(s).
Run #1: 1 invoice + 1 email + 1 message → Run #2: 1 invoice + 1 email + 1 message
- Create Invoice (payment): idempotency fail, reversibility low. Second delivery creates another payment object. Hard to reverse cleanly.
- Email Customer (email): idempotency fail, reversibility low. Second delivery sends the email again. You cannot unsend it.
- Notify Slack (message): idempotency fail, reversibility low. Second delivery posts again. Chat noise, hard to scrub.
Stress simulations
4 happy-path steps · 3 external action(s)
- FAILDuplicate trigger delivery
Second delivery still sent 1 email(s) and ran 1 payment action(s).
- FAILSpam burst (10 triggers)
Across 10 quick fires, 30 outbound actions ran. 27 of them were after the first run. Example: 10 copies of the same email went to customer@example.com (subject: "Your invoice").
- FAILMissing email fields
Missing email caused send to abort mid-flow - customer path is not guarded cleanly.
- FAILFail after first side effect
After stripe_invoice_create, a later step failed - partial completion risk confirmed in simulation.
- FAILEmpty trigger payload
Empty trigger data still produced 1 external action(s).
- FAILFake-company stress (50 runs)
Across 50 simulated runs: 50 near-identical chat messages posted.
- FAILLoud rate-limit (100 triggers)
Loud rate-limit: provider returned 429 after Payments 8/8 (92 throttle hit(s)).
- FAILSilent quota drain (100 triggers)
Silent quota drain: Email / ESP 100 calls (soft 5); Payments 100 calls (soft 3); Team chat 100 calls (soft 8) — 2800 budget unit(s) burned with no hard stop.
Findings & fixes
critical (7)
1. "Create Invoice" can fail with no backup path
stripe can fail (timeout, rate limit, auth). No continue-on-fail / error output, and no global error workflow.
Why it matters: Money or invoices can stop mid-flight after earlier steps already ran - books and reality diverge.
What to do: Turn on retry for transient SMTP/API errors, and route hard failures to an error branch that alerts someone.
2. "Create Invoice" retries can create duplicates
Retry is on (up to 5 tries) on a create/send action. Each retry can fire again.
Why it matters: Flaky networks turn into double emails, double Slack pings, or double charges.
What to do: Before create/send: check "already processed?" using a unique event/message id. Prefer upserts over creates.
3. Webhook "Payment Webhook" can fire side effects twice
Providers often retry webhooks. Downstream create/send nodes have no obvious "already handled this event?" check.
Why it matters: Duplicate delivery can mean double charges or double customer emails.
What to do: Store the provider event id; if you've seen it, skip every create/send node.
4. Ran 3 actions twice on a retry
We sent the same trigger twice, like a webhook that retries or someone double-clicking submit.
Why it matters: 1 email(s) and 1 payment action(s) ran again on the second delivery. In production that means duplicate emails, charges, or messages for the same event.
What to do: Before you send email or charge a card, store the order/message id. If that id was already handled, skip. Put a short queue in front of bursts so one webhook storm cannot spam.
5. Sent the same email 10 times in a burst
We hit the workflow 10 times quickly, like a retry storm or accidental spam of the same webhook.
Why it matters: 27 of those actions happened after the first run. Example from the ledger: 50 near-identical chat messages posted.
What to do: Before you send email or charge a card, store the order/message id. If that id was already handled, skip. Put a short queue in front of bursts so one webhook storm cannot spam.
6. Under heavy retry, saw 50× the same outbound action
We fired the trigger 50 times into one fake company to see what piles up.
Why it matters: 50 near-identical chat messages posted.
What to do: Before you send email or charge a card, store the order/message id. If that id was already handled, skip. Put a short queue in front of bursts so one webhook storm cannot spam.
7. Burst kept succeeding and burned budget quietly
Same big burst, but the fake provider never hard-failed. It just kept accepting calls.
Why it matters: In production you may not notice until the API bill spikes or the account is empty.
What to do: Add spend/quota alerts and a daily cap. Do not rely on the provider to fail loudly.
high (6)
1. Nobody gets alerted when this workflow fails
There is no Error Trigger and no workflow-level Error Workflow. If a node dies mid-run, the failure can vanish into the execution log.
Why it matters: You may think an email or invoice finished when it actually stopped halfway - and no one is paged.
What to do: In n8n Settings, set an Error Workflow (or add an Error Trigger flow) that Slack/emails your team with the failed run link.
2. "Email Customer" can fail with no backup path
gmail can fail (timeout, rate limit, auth). No continue-on-fail / error output, and no global error workflow.
Why it matters: Customers get silence, duplicate retries, or a half-finished experience with no internal alert.
What to do: Turn on retry for transient SMTP/API errors, and route hard failures to an error branch that alerts someone.
3. "Notify Slack" can fail with no backup path
slack can fail (timeout, rate limit, auth). No continue-on-fail / error output, and no global error workflow.
Why it matters: Customers get silence, duplicate retries, or a half-finished experience with no internal alert.
What to do: Turn on retry for transient SMTP/API errors, and route hard failures to an error branch that alerts someone.
4. Tried to send email with a missing recipient
We removed the email address from the trigger payload.
Why it matters: Missing email caused send to abort mid-flow - customer path is not guarded cleanly.
What to do: Add an IF check: only send when the recipient field is present and looks like an email.
5. Earlier step already sent, then a later step failed
We let the first outbound action succeed, then forced a later one to fail.
Why it matters: Half-finished runs are hard to undo. Example: approval email went out, customer reply never did.
What to do: Decide what to do when a later step dies: alert someone, retry safely, or undo the earlier action.
6. Provider started rejecting calls mid-burst (rate limit)
We slammed the workflow with a big burst. The fake provider answered with hard stops (like HTTP 429).
Why it matters: In production the run fails halfway. Some emails/charges may already be out, then retries make a mess.
What to do: Put a queue in front of outbound sends. Back off when the provider says slow down. Alert when you hit the cap.
medium (2)
1. "Email Customer" recipient comes from live data
To is dynamic: ={{$json.customerEmail}}. If that field is empty or wrong, the send still runs.
Why it matters: A bad From/email field can bounce, leak to the wrong person, or fail silently depending on SMTP settings.
What to do: Add an IF node: only send when the address looks like a real email. Otherwise alert ops.
2. Empty trigger data still reached outbound steps
We fired the workflow with almost no payload.
Why it matters: Empty trigger data still produced 1 external action(s).
What to do: Stop the workflow early when required fields are missing.