Validate input before it poisons the run
Retries and error workflows catch failures. But some of the worst production problems are not failures at all, they are bad data that flows through cleanly and corrupts something downstream. A webhook fires with a missing email field. An upstream API returns an empty array. n8n happily processes it and writes garbage to the client's database.
Guard against this with an IF node right after your trigger. Check the fields you depend on before doing any real work. If the data is bad, route it to a branch that logs it and stops, instead of letting it continue.
{{ $json.email }} is not empty AND {{ $json.email }} contains @
Route the true branch into your real logic. Route the false branch into a small dead-end that records the bad payload (a database row, a logged message) so you can see what came in and why it was rejected. The client's data stays clean, and you get a paper trail. This validate-first pattern matters more as you chain automations together, and it is one of the reasons n8n holds up better than some alternatives for real client work, which the Make versus n8n comparison gets into.
Make writes idempotent so retries are safe
There is a trap hiding in the retry advice above. If a node writes data and then a later step fails, a retry can write the same data twice. You retried to be safe, and instead you created a duplicate invoice. Retries are only safe when the operations they repeat are idempotent, meaning running them twice produces the same result as running them once.
So design writes to be idempotent. A few concrete moves:
Use an external ID or upsert instead of a blind insert, so a second write updates the same row rather than creating a new one. Check whether a record exists before creating it. For emails and messages, dedupe on a key (the lead's email plus the date) so a re-run doesn't fire the same message twice. And keep a record of what you have already processed, so the workflow can skip work it already did.
Get this right and you can retry aggressively without fear. Get it wrong and your retries become the bug.
Test the failure paths before you hand it off
Most freelancers test the happy path, see it work, and ship. Then the first real failure surprises everyone. Before handoff, deliberately break things. Point a node at a bad URL and confirm your error workflow fires. Send the webhook a payload with a missing field and confirm the IF branch catches it. Pull a credential and watch what happens.
Run through this checklist before you call a client workflow done:
| Check | What you are proving |
|---|
| Every external node has Retry On Fail on | Transient errors self-heal |
| Error workflow attached and tested | A real failure reaches you |
| IF validation on all trigger inputs | Bad data can't get downstream |
| Writes are idempotent | Retries can't duplicate records |
| Credentials documented for the client | Handoff doesn't strand them |
The credentials line matters at handoff. The client owns the accounts, so the workflow should run on their credentials, not yours. Document which credentials each workflow needs and where they live, so the client (or the next freelancer) isn't stranded. If the automation is a lead source feeding their pipeline, that reliability is the whole product, the same way job notifications are only useful if they never miss a job.
Where this gets you paid
Reliability is a positioning tool. Two freelancers can both build the same n8n workflow. The one who bakes in retries, an error workflow, input validation, and idempotent writes can honestly promise it will keep running, and can charge for that promise. The one who ships the happy path is one outage away from a refund request.
The demand is there. With AI Agent Development posting hundreds of new freelance jobs a month, the builders who win the repeat work and the retainers are the ones whose automations don't break. Build for the failure cases first, and the reliable handoff becomes your differentiator.
Frequently Asked Questions
What Retry On Fail settings should I use in n8n?
For most external calls, turn on Retry On Fail with 3 tries and a 1000ms wait between attempts (retryOnFail: true, maxTries: 3, waitBetweenTries: 1000). Bump the wait to 2000-5000ms for APIs that rate-limit hard, like the major AI APIs under load. Leave it off for nodes that do no I/O, since retrying pure logic just hides a bug.
How do I get notified when an n8n workflow fails?
Build a separate error workflow that starts with an Error Trigger node, then attach it to your production workflow under Settings, Error Workflow. When a node fails after its retries, n8n runs the error workflow with the full error context, so you can send yourself the workflow name, failed node, and error message over Slack, Discord, or email. One error workflow can serve every workflow you build for that client.
Do I need to make n8n nodes idempotent?
Yes, if those nodes write data and you have retries on. A retry can repeat a write and create a duplicate, so use upserts or external IDs, check for existing records before inserting, and dedupe messages on a stable key. Idempotent writes are what make aggressive retries safe instead of dangerous.
How do I stop bad data from breaking an n8n workflow?
Add an IF node right after your trigger that checks the fields you depend on before any real work runs. Route valid data into your main logic and route invalid data into a dead-end branch that logs the bad payload and stops. This keeps malformed webhooks and empty API responses from flowing downstream and corrupting the client's data.
What makes an n8n workflow client-ready versus just working?
A working workflow runs on the happy path in a demo. A client-ready one fails loudly, retries transient errors, validates its input, writes idempotently, and comes with documented credentials the client owns. The difference is whether it keeps running unattended for months, which is what a client is actually paying for.