Crash Labs
n8n stress-test report
Deploy gate
FAIL
Not production-ready yet
"Invoice on payment webhook" should not go live yet: 6 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
- 6
- High
- 5
- 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
- Retry survival: 3 side effect(s) still fire on delivery #2 (3 idempotency fail · 3 low reversibility).
- Simulation scenarios: 0/6 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 identical emails to ada@example.com with subject "Your invoice".
- CRITICAL: 47 Stripe stripe:invoice(s) for customer key "ada@example.com".
- HIGH: 2 identical emails to cara@example.com with subject "Your invoice".
- HIGH: 2 Stripe stripe:invoice(s) for customer key "cara@example.com".
Retry survival · same event twice
Retry survival: 1 payment · 1 email · 3 side effect(s) survived run 2.
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
Retry survival: 1 payment · 1 email · 3 side effect(s) survived run 2.
- FAILSpam burst (10 triggers)
10 trigger fires produced 30 external action(s) (3+3+3+3+3+3+3+3+3+3 per run). 27 still fired after delivery #1. Ledger: 10 identical emails to customer@example.com with 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.
Findings & fixes
critical (6)
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. Retry survival: 3 side effect(s) still fire on delivery #2
Same trigger delivered twice (webhook retry / double submit). Side effects are scored for idempotency and reversibility.
Why it matters: Retry survival: 1 payment · 1 email · 3 side effect(s) survived run 2.
What to do: Add dedupe keys, idempotency, and rate limiting / a queue before outbound sends and Stripe writes.
5. Simulation failed: Spam burst (10 triggers)
Trigger is hit 10 times in a burst into one fake company.
Why it matters: 10 trigger fires produced 30 external action(s) (3+3+3+3+3+3+3+3+3+3 per run). 27 still fired after delivery #1. Ledger: 10 identical emails to customer@example.com with subject "Your invoice".
What to do: Add dedupe keys, idempotency, and rate limiting / a queue before outbound sends and Stripe writes.
6. Fake-company ledger: 50× blast radius
Across 50 simulated runs: 50 near-identical chat messages posted.
Why it matters: 50 near-identical chat messages posted.
What to do: Treat every outbound node as dangerous under retries. Require an idempotency key (order id / message id) before send/charge/write.
high (5)
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. Simulation failed: Missing email fields
Trigger payload has empty/null email fields.
Why it matters: Missing email caused send to abort mid-flow - customer path is not guarded cleanly.
What to do: Validate recipient with an IF before any send node.
5. Simulation failed: Fail after first side effect
After the first external action, a later outbound step is forced to fail.
Why it matters: After stripe_invoice_create, a later step failed - partial completion risk confirmed in simulation.
What to do: Add compensating actions and alerts when a later step dies after an earlier side effect.
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. Simulation failed: Empty trigger payload
Trigger yields nearly empty JSON.
Why it matters: Empty trigger data still produced 1 external action(s).
What to do: Add guards so empty/bad trigger data cannot reach outbound nodes.