I remember the first time I saw "Workflow Execution Failed" on a production server at 2 AM. My heart sank. I had no idea where to start. After 3 hours of debugging, I found the issue — a missing OAuth refresh token. That night taught me the systematic approach I am sharing with you today.
Short on time? Pick your error from the table of contents below and jump straight to the fix. I have debugged this error across self-hosted Docker instances, Oracle Cloud VPSes, n8n Cloud, and Railway deployments. The error always has a specific cause — but you have to know where to look and what to look for. This guide gives you a systematic approach to find the real error in under two minutes and apply the exact fix it needs.
Jump to Your Error Fix
- Fix #1 — Node Timeout
- Fix #2 — Expression Errors
- Fix #3 — Credential Errors
- Fix #4 — Data Type Mismatches
- Fix #5 — Rate Limiting
- Fix #6 — Code Node JavaScript Errors
- Fix #7 — Memory Exhaustion
- Fix #8 — Sub-workflow Failures
- Fix #9 — Silent Failures
- Cloud vs Self-Hosted
- Proactive Measures
- Free Error Logger
- FAQ
- More Guides
Quick Reference: Error → Fix
- 🔴 "ETIMEDOUT" → Increase timeout in HTTP Request node (Options → Timeout).
- 🔴 "Cannot read properties of undefined" → Use optional chaining:
{{ $json.field?.subfield ?? 'fallback' }}. - 🔴 "401 Unauthorized" → Reconnect credential in n8n Settings → Credentials.
- 🔴 "400 Bad Request" → Check API documentation for required fields; use an IF node to validate.
- 🔴 "429 Too Many Requests" → Add a Wait node or Split in Batches to slow down requests.
- 🔴 "JavaScript syntax error" → Wrap Code node in try-catch; return
[{ json: {...} }]. - 🔴 "Killed / OOM" → Increase Node.js memory:
NODE_OPTIONS=--max-old-space-size=2048. - 🔴 "Sub-workflow failed" → Check the child workflow's execution log separately.
- 🔴 Execution shows "Failed" but every node looks green → See Fix #9 — Silent Failures.
- 🔴 "Check URL node success error" → See Fix #4 and the dedicated guide on Check URL Node.
"Workflow Execution Failed" is a wrapper message. The real error is almost always inside the execution log. Open it with these steps, then match the error to one of the nine fixes below.
- Click the Executions tab in the left sidebar (or the clock icon).
- Click the red failed execution entry to open it.
- Click the red-bordered node — that is the node that caused the failure.
- Read the Output or Error panel on the right. The real error message is there.
- Match the message to the nine categories in this guide and apply the fix.
Step 0: How to Actually Read n8n's Execution Log
Before diving into fixes, you need to master the execution log. This is the tool that turns "Workflow Execution Failed" — a useless message on its own — into a specific, actionable error you can fix in minutes. I have debugged this error repeatedly across self-hosted Docker instances, Oracle Cloud VPSes, n8n Cloud, and Railway deployments. In the large majority of cases, the fix takes under 5 minutes once you know where to look.
The Five-Step Debugging Process
Open the Executions list
Click the clock icon in the left sidebar (or Executions tab). You will see a list of all recent runs, each showing status (green = success, red = failed), timestamp, and duration. Click the red failed entry.
Identify the failing node
The execution view shows your workflow canvas with color-coded nodes. Green nodes ran successfully. The red-bordered node is where execution stopped. Click it.
Read the error message
In the node's Output panel (right side), switch to the Error tab. You will see the specific error message, HTTP status code (if it was an API call), and sometimes a stack trace. This is your real clue — not the generic banner.
Check the input data of the failing node
Switch to the Input tab of the failing node. This shows exactly what data the node received. Many errors happen because the input data is missing a field, has an unexpected type, or is null — visible here immediately.
Check n8n server logs for infrastructure errors
If the node error is generic or absent, the real error may be at the infrastructure level. Check server logs: docker logs n8n --tail 50 or journalctl -u n8n -n 50. Out-of-memory kills and database errors appear here, not in the UI. This step only applies to self-hosted instances — see Cloud vs Self-Hosted below.
The single most common debugging mistake in n8n is trying to fix the workflow without first reading the execution log. The log gives you a specific error message in the large majority of cases. Always start there — it takes 20 seconds and eliminates hours of guessing. — William Ayadi, TriggerWorkflow
Error Message Quick-Reference Table
| Error Message Contains | Category | Fix Section |
|---|---|---|
| "ETIMEDOUT" / "ESOCKETTIMEDOUT" / "timeout" | Node Timeout | Fix #1 |
| "Cannot read properties of undefined" / "is not defined" | Expression Error | Fix #2 |
| "401 Unauthorized" / "403 Forbidden" / "invalid_grant" | Credential Failure | Fix #3 |
| "400 Bad Request" / "422 Unprocessable" / "missing field" | Data / API Error | Fix #4 |
| "429 Too Many Requests" / "rate limit exceeded" | Rate Limiting | Fix #5 |
| "JavaScript syntax error" / "Unexpected token" | Code Node Error | Fix #6 |
| "Killed" / "OOM" / "heap out of memory" | Memory / Resource | Fix #7 |
| "Sub-workflow execution failed" / "called workflow" | Sub-workflow | Fix #8 |
| No error text at all, execution just shows red | Silent Failure | Fix #9 |
Fix #1: Node Timeout Errors
n8n enforces execution time limits on every node. An HTTP Request node that waits for a slow API, a Wait node set to too long a duration, or a Code node with an infinite loop — all trigger a timeout that kills the execution.
Identifying a Timeout Error
The error message will contain: ETIMEDOUT, ESOCKETTIMEDOUT, socket hang up, or simply timeout. The node type is almost always HTTP Request, Wait, or Code.
Fix for HTTP Request Node Timeouts
Open the HTTP Request node → Options tab → set Timeout to 60000 (60 seconds) or 120000 (120 seconds). The default is 10000ms (10 seconds) — too short for any AI API or slow third-party service.
Fix for Global Execution Timeout (Self-Hosted)
Add this to your .env file or Docker environment, then restart n8n:
# Default is 3600 seconds (1 hour). Increase for long-running workflows. EXECUTIONS_TIMEOUT=7200 EXECUTIONS_TIMEOUT_MAX=10800 # For HTTP Request node specifically: N8N_DEFAULT_TIMEOUT=120000 # in milliseconds
For AI API calls (OpenAI, Claude, Gemini): These can take 30–90 seconds for large prompts. Always set HTTP Request timeout to at least 120,000ms (2 minutes) when calling any generative AI endpoint. The default 10-second timeout will fail every long prompt.
Fix #2: Expression Errors (The Most Common Cause)
Expression errors are the single most frequent cause of execution failures in n8n. They happen when you reference a field that does not exist, use the wrong variable, or write a JavaScript expression with a type error.
The Three Expression Error Patterns
Cause: You wrote
{{ $json.user.email }} but the user object is null or does not exist for this item.✅ Fix: Use optional chaining:
{{ $json.user?.email ?? 'unknown' }} — this returns 'unknown' instead of crashing when user is null.
Cause: You referenced a node by the wrong name, e.g.
{{ $('Gmail Trigger').item.json.subject }} but the node is named "Gmail" not "Gmail Trigger".✅ Fix: Node names in expressions must match exactly. Open the source node, copy its exact name from the title bar, and use that in your expression.
Cause: Your test data had all fields present. Real incoming data is missing one field. The expression evaluates to
undefined and downstream nodes crash.✅ Fix: Always provide a fallback value:
{{ $json.email ?? '[email protected]' }}. Never assume all fields will always be present.
Defensive Expression Patterns for Production
// Safe string field access with fallback {{ $json.customer?.name ?? 'Unknown Customer' }} // Safe nested access — won't crash if address is null {{ $json.customer?.address?.city ?? 'No City' }} // Safe number conversion {{ Number($json.price) || 0 }} // Safe array length check {{ Array.isArray($json.items) ? $json.items.length : 0 }} // Safe date formatting {{ $json.created_at ? new Date($json.created_at).toISOString() : new Date().toISOString() }}
Learn how to build complete AI workflows — email classifiers, lead scorers, content repurposers — with free JSON templates and real cost breakdowns. The foundation for everything on this site.
Fix #3: Credential Errors (Expired OAuth and Wrong Keys)
Credential errors produce 401 or 403 HTTP responses, or provider-specific messages like invalid_grant, access_token_expired, or invalid API key. They are extremely common in long-running automations because OAuth tokens expire.
How OAuth Tokens Expire in n8n
Google, Microsoft, Slack, and most OAuth2 services issue short-lived access tokens (typically 1 hour) and long-lived refresh tokens (months to years). n8n automatically refreshes access tokens using the refresh token. But if:
- The user revokes n8n's access in their Google Account settings
- The refresh token itself expires (Google expires refresh tokens after 6 months of non-use)
- The OAuth app is deleted or its scopes are changed
...then n8n cannot refresh the token and every execution using that credential fails with a 401 error. For the official list of credential types and setup requirements, see the n8n credentials documentation.
Fix: Re-authenticate the Credential
Go to n8n Settings → Credentials → find the failing credential → click Reconnect (for OAuth2) or update the API key. For Google credentials, you may need to fully revoke and re-authorize.
Pro Tip: Set Up a Credential Health Check Workflow
Build a weekly Schedule Trigger workflow that makes a simple API call with each critical credential (e.g., list 1 Google Drive file, fetch 1 Gmail message). If it fails, send yourself a Telegram alert. Catching credential failures before they affect production workflows saves hours of downstream debugging.
Fix #4: Data Type Mismatches and API Request Errors
n8n passes data between nodes as JSON objects. When a node receives data in the wrong format — a string where a number is expected, an object where an array is required, or a null where a non-null value is mandatory — the execution fails.
One of the most common searches I see is "n8n check url node success error output". This happens when the HTTP Request node returns a 200 OK but the body is empty or contains an error. The fix is to add a Code node after the HTTP Request to validate the response body — see the example below.
Type Conversion in a Code Node
// String → Number (price field from Typeform often comes as string) const price = parseFloat($input.item.json.price_text) || 0; // Number → String (for Telegram message formatting) const priceStr = price.toFixed(2); // Object → JSON string (for HTTP Request body that expects a string) const payload = JSON.stringify({ amount: price, currency: 'USD' }); // Comma-separated string → Array (Typeform checkbox answers) const tags = ($input.item.json.tags_csv || '').split(',').map(t => t.trim()).filter(Boolean); return [{ json: { price_number: price, price_string: priceStr, tags_array: tags, api_payload: payload } }];
Missing Required Fields in API Calls
When calling any external API (Stripe, OpenAI, Shopify, HubSpot), a 400 or 422 response means the API rejected your request because a required field is missing or invalid. The fix process:
- Open the failing HTTP Request node and click the Output panel in the failed execution. The response body will list the exact missing field.
- Open the API's official documentation and verify the required fields for that endpoint.
- Add an IF node before the API call to validate that all required fields are present and non-empty before sending the request.
For a deeper dive into this specific issue, see the dedicated guide: n8n Check URL Node: How To Fix Success Error & Execution Timeout.
Fix #5: API Rate Limiting (429 Errors)
A 429 error means you are sending requests faster than the API allows. OpenAI, Google, Stripe, and every major API provider enforces rate limits. In n8n, this typically happens when a workflow loops over many items and calls an API for each one without any delay.
The Rate Limit Fix Toolkit
OpenAI rate limits in 2026: Free tier users on gpt-4o-mini are limited to 3 requests per minute. Paid tier allows up to 500 RPM. If your workflow loops over more than 3 items and calls OpenAI for each, you will always hit 429 on the free tier without rate limiting protection.
The three-layer approach to prevent rate limit failures:
- Layer 1 — Wait node: Add a Wait node (1–2 seconds) between loop iterations to space out API calls. Simple and effective for moderate volumes.
- Layer 2 — Retry on Fail: On the HTTP Request or AI node, enable Retry on Fail (Options → Retry on Fail → 3 retries, 5 seconds wait). n8n will automatically retry 429 responses with a delay.
- Layer 3 — Split in Batches: Use the Split in Batches node to process items in groups of 5 or 10, with a Wait node between batches. This is the most reliable approach for bulk operations.
Correct node order for rate-limit-safe batch processing: [Trigger] → [Split in Batches: size=5] → [HTTP Request / AI node] ↑ ↓ └──── [Wait: 2 seconds] ←──────┘ Split in Batches node settings: Batch Size: 5 (process 5 items at once) Reset: false (continue from where it left off on retry) Wait node settings: Amount: 2 Unit: Seconds
This guide builds a multi-AI workflow that processes content at scale — with proper rate limit handling, retry logic, and batch processing baked in from the start.
Fix #6: Code Node JavaScript Errors
The Code node gives you full JavaScript inside n8n — which is powerful, but also means JavaScript errors can crash your workflow. The most common mistakes come from incorrect return format, missing semicolons, or using async functions without await.
The Correct Code Node Return Format
// ❌ WRONG — returning a plain object crashes the workflow return { name: 'Alice', email: '[email protected]' }; // ❌ WRONG — returning an object inside json but not in an array return { json: { name: 'Alice' } }; // ✅ CORRECT — must return an array of objects, each with a json key return [{ json: { name: 'Alice', email: '[email protected]' } }]; // ✅ CORRECT — processing multiple input items return $input.all().map(item => ({ json: { name: item.json.name, email: item.json.email?.toLowerCase() ?? 'unknown' } })); // ✅ CORRECT — error handling with try-catch try { const parsed = JSON.parse($input.item.json.raw_data); return [{ json: { parsed_data: parsed, success: true } }]; } catch (error) { return [{ json: { error: error.message, success: false, raw: $input.item.json.raw_data } }]; }
Debugging Code Node Errors
Add console.log() statements to inspect values at runtime. The output appears in your server logs. For Docker: docker logs n8n --tail 30 --follow. For systemd: journalctl -u n8n -f.
Fix #7: Memory and Resource Exhaustion (Self-Hosted)
On self-hosted n8n instances, workflows that process large files or loop over thousands of items can exhaust available memory. When this happens, the OS kills the n8n process mid-execution. The workflow shows "Execution Failed" with no node-level error — because the entire process died.
Symptoms of Memory-Related Failures
- Server logs show
KilledorOOMKilledimmediately after execution failure. - Docker shows
OOM killer invokedin system logs (dmesg | grep -i oom). - Execution fails on large datasets but works perfectly on small test sets.
- n8n restarts itself automatically after the failure.
The Three-Layer Fix for Memory Issues
- Increase Node.js heap size: Add
NODE_OPTIONS=--max-old-space-size=2048to your environment variables. This gives Node.js 2GB of heap. Restart n8n after adding. - Batch large datasets: Use the Split in Batches node to process items in groups of 50–100 instead of all at once. This keeps memory usage flat regardless of dataset size.
- Upgrade VPS RAM: Minimum recommended RAM for production n8n is 2GB. For workflows processing files or images, 4GB is better. Oracle Cloud free tier offers 1GB — sufficient for light workflows, insufficient for heavy processing.
# Increase Node.js heap (in MB) — add to .env or Docker environment NODE_OPTIONS=--max-old-space-size=2048 # Limit concurrent executions to prevent memory spikes EXECUTIONS_PROCESS=main N8N_CONCURRENCY_PRODUCTION_LIMIT=10 # Apply changes: # Docker: docker compose down && docker compose up -d # systemd: sudo systemctl restart n8n
Fix #8: Sub-Workflow and Execute Workflow Failures
When you use the Execute Workflow node to call another workflow, a failure in the child workflow propagates back to the parent and shows as "Workflow Execution Failed" in the parent's execution log. The real error is inside the child workflow's execution, not the parent's.
How to Find the Real Error in a Sub-Workflow
- Note the execution ID shown in the parent's failing Execute Workflow node output.
- Go to the Executions list, filter by the child workflow's name, and find the execution with that ID.
- Open the child execution log and find the red-bordered node there — that is the real error.
Best practice for sub-workflows: Add an Error Trigger node inside every sub-workflow that handles failures independently. This prevents one failing sub-workflow from silently crashing all parent workflows that call it.
AI Agents in n8n use sub-workflow patterns heavily. This guide shows how to structure them to avoid cascading failures and ensure each component handles its own errors gracefully.
Fix #9: Silent Failures (No Error Message At All)
The hardest case: the execution shows red, but every node appears green when you open it, and the Error tab is empty. This is a silent failure, and it is the one gap most n8n troubleshooting guides skip entirely.
The Three Most Common Causes
- The trigger itself failed before any node ran. Check the Trigger node's own execution history separately — Webhook and Schedule triggers can fail silently if the underlying service (Blogger, a third-party webhook sender) never delivered a valid payload.
- The process was killed externally (VPS out-of-memory, a Docker restart policy, or a hosting provider's maintenance window) mid-execution, before n8n could write an error to the node. Check
dmesgor your hosting provider's status page for the exact timestamp of the failed execution. - A workflow-level setting silently swallowed the error. If Continue on Fail is enabled on the last node in the chain, the workflow can report "success" or an empty failure with no visible cause. Audit every node's Settings tab for this option.
Fastest way to catch silent failures: Deploy the free Error Logger workflow below and set every production workflow's Settings → Error Workflow to point to it. Even a silent failure at the process level will show up as a missing execution in your Google Sheet log — the absence of an expected row is itself the alert.
n8n Cloud vs Self-Hosted: Does This Guide Apply to You?
Most troubleshooting content online is written for one hosting type and silently assumes you are using the other. Here is exactly where this guide's fixes apply:
| Fix | n8n Cloud | Self-Hosted (Docker / VPS) |
|---|---|---|
| Fix #1 Timeout | Applies | Applies |
| Fix #2 Expression Errors | Applies | Applies |
| Fix #3 Credentials | Applies | Applies |
| Fix #4 Data / API Errors | Applies | Applies |
| Fix #5 Rate Limiting | Applies | Applies |
| Fix #6 Code Node Errors | Applies | Applies |
| Fix #7 Memory Exhaustion | Not applicable — infrastructure is managed for you | Applies directly |
| Fix #8 Sub-workflow Failures | Applies | Applies |
| Fix #9 Silent Failures (server-level) | Limited — no server log access | Applies directly |
If you are on n8n Cloud and hit a silent failure with no server logs to check, your two remaining tools are the Error Logger workflow (below) and n8n's own support channel, since you cannot inspect the underlying process yourself.
Proactive Measures: How to Prevent "Workflow Execution Failed" Before It Happens
While fixing errors is essential, preventing them is even better. The following best practices will reduce the frequency of execution failures in your n8n workflows.
1. Always Validate Incoming Data
Add an IF node or Code node at the start of every workflow to check that required fields exist and are of the correct type. This is especially critical for webhook triggers where the incoming payload structure can vary unpredictably.
2. Use "Continue on Fail" for Non-Critical Paths
For HTTP Request nodes that call external APIs, enable Continue on Fail in the node's Settings. Then connect an IF node to check for errors and route the execution accordingly (e.g., log the error and continue with a fallback instead of failing the whole workflow).
3. Implement Idempotency
Design workflows so that re-running them does not duplicate actions. For example, use unique IDs to check if an order has already been processed before sending a confirmation email. This prevents duplicate operations that can cause downstream errors.
4. Set Up a Staging Environment
Test new workflows in a separate n8n instance or using test data before deploying them to production. This is particularly important for workflows that modify data or send real notifications.
5. Monitor Execution Trends
Use the Error Logger workflow from this article to track failures over time. If you see a sudden spike in a particular error type, investigate immediately — it may indicate a change in an external API or a credential about to expire.
My experience: Workflows that implement these five proactive measures see a noticeable drop in unexpected execution failures within the first month. Prevention is consistently cheaper than debugging.
Free Download: Automatic Error Logger Workflow
Instead of discovering workflow failures hours later, deploy this Error Trigger workflow to log every failure to Google Sheets and send you an instant Telegram alert. Import it once and it monitors all your workflows automatically.
n8n Error Logger — Telegram + Google Sheets
Error Trigger + Telegram + Google Sheets · Monitors all workflows · Import in 60 seconds
{
"name": "Error Logger — Telegram + Google Sheets",
"nodes": [
{
"parameters": {},
"name": "Error Trigger",
"type": "n8n-nodes-base.errorTrigger",
"typeVersion": 1,
"position": [240, 300]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "1",
"name": "workflow_name",
"value": "={{ $json.workflow.name }}",
"type": "string"
},
{
"id": "2",
"name": "error_message",
"value": "={{ $json.execution.error.message ?? 'Unknown error' }}",
"type": "string"
},
{
"id": "3",
"name": "node_name",
"value": "={{ $json.execution.error.node?.name ?? 'Unknown node' }}",
"type": "string"
},
{
"id": "4",
"name": "execution_id",
"value": "={{ $json.execution.id }}",
"type": "string"
},
{
"id": "5",
"name": "failed_at",
"value": "={{ new Date().toISOString() }}",
"type": "string"
}
]
},
"options": {}
},
"name": "Format Error Data",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [460, 300]
},
{
"parameters": {
"chatId": "YOUR_TELEGRAM_CHAT_ID",
"text": "🔴 *n8n Workflow Failed*\n\n📋 Workflow: {{ $json.workflow_name }}\n⚠️ Error: {{ $json.error_message }}\n🔧 Node: {{ $json.node_name }}\n🆔 Execution: {{ $json.execution_id }}\n🕐 Time: {{ $json.failed_at }}",
"additionalFields": { "parse_mode": "Markdown" }
},
"name": "Telegram Alert",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [680, 220],
"credentials": { "telegramApi": { "id": "YOUR_TELEGRAM_CREDENTIAL_ID" } }
},
{
"parameters": {
"operation": "append",
"documentId": { "value": "YOUR_GOOGLE_SHEET_ID", "mode": "id" },
"sheetName": { "value": "Error Log", "mode": "name" },
"columns": {
"mappingMode": "defineBelow",
"value": {
"Workflow": "={{ $json.workflow_name }}",
"Error": "={{ $json.error_message }}",
"Node": "={{ $json.node_name }}",
"Execution ID": "={{ $json.execution_id }}",
"Timestamp": "={{ $json.failed_at }}"
}
},
"options": {}
},
"name": "Log to Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [680, 400],
"credentials": { "googleSheetsOAuth2Api": { "id": "YOUR_GOOGLE_CREDENTIAL_ID" } }
}
],
"connections": {
"Error Trigger": { "main": [[{ "node": "Format Error Data", "type": "main", "index": 0 }]] },
"Format Error Data": {
"main": [[
{ "node": "Telegram Alert", "type": "main", "index": 0 },
{ "node": "Log to Google Sheets", "type": "main", "index": 0 }
]]
}
},
"active": false,
"settings": { "executionOrder": "v1" }
}
After importing, replace YOUR_TELEGRAM_CHAT_ID, YOUR_TELEGRAM_CREDENTIAL_ID, YOUR_GOOGLE_SHEET_ID, and YOUR_GOOGLE_CREDENTIAL_ID with your actual values. Then activate the workflow. Finally, in each of your production workflows go to Settings → Error Workflow and select this Error Logger.
Frequently Asked Questions
{{ $json.user.email }} crashes when user is null. Fix it with optional chaining: {{ $json.user?.email ?? 'fallback' }}. Always provide a fallback value for any field that might not always be present in real data.NODE_OPTIONS=--max-old-space-size=2048 in your environment, the Split in Batches node for large datasets, and a minimum 2GB VPS. Oracle Cloud free tier's 1GB RAM is insufficient for heavy workflows.console.log('Debug:', JSON.stringify($input.item.json)) inside your Code node — the output appears in your server logs. Check with docker logs n8n --tail 30 or journalctl -u n8n -n 30. Also wrap your logic in try-catch so that errors return a descriptive message instead of crashing the entire workflow.Continue Learning on TriggerWorkflow
If you found this guide useful, here are more articles to help you master n8n automation:
Zero Blind Spots: Deploy the Error Logger Now
Import the free Error Logger workflow above, activate it, and get instant Telegram alerts for every future failure — before your users notice.
Browse More Error Fixes →