14 KiB
Error Patterns - Python Code Node
Common Python Code node errors and how to fix them.
Error Overview
Top 5 Python Code Node Errors:
- ModuleNotFoundError - Trying to import external libraries (Python-specific)
- Empty Code / Missing Return - No code or return statement
- KeyError - Dictionary access without .get()
- IndexError - List access without bounds checking
- Incorrect Return Format - Wrong data structure returned
These 5 errors cover the majority of Python Code node failures.
Error #1: ModuleNotFoundError (MOST CRITICAL)
Frequency: Very common in Python Code nodes
What it is: Attempting to import external libraries that aren't available.
The Problem
# ❌ WRONG: External libraries not available
import requests # ModuleNotFoundError: No module named 'requests'
import pandas # ModuleNotFoundError: No module named 'pandas'
import numpy # ModuleNotFoundError: No module named 'numpy'
import bs4 # ModuleNotFoundError: No module named 'bs4'
import pymongo # ModuleNotFoundError: No module named 'pymongo'
import psycopg2 # ModuleNotFoundError: No module named 'psycopg2'
# This code will FAIL - these libraries are not installed!
response = requests.get("https://api.example.com/data")
The Solution
Option 1: Use JavaScript Instead (Recommended for 95% of cases)
// ✅ JavaScript Code node with $helpers.httpRequest()
const response = await $helpers.httpRequest({
method: 'GET',
url: 'https://api.example.com/data'
});
return [{json: response}];
Option 2: Use n8n HTTP Request Node
# ✅ Add HTTP Request node BEFORE Python Code node
# Access the response in Python Code node
response = _input.first()["json"]
return [{
"json": {
"status": response.get("status"),
"data": response.get("body"),
"processed": True
}
}]
Option 3: Use Standard Library Only
# ✅ Use urllib from standard library (limited functionality)
from urllib.request import urlopen
from urllib.parse import urlencode
import json
# Simple GET request (no headers, no auth)
url = "https://api.example.com/data"
with urlopen(url) as response:
data = json.loads(response.read())
return [{"json": data}]
Common Library Replacements
| Need | ❌ External Library | ✅ Alternative |
|---|---|---|
| HTTP requests | requests |
Use HTTP Request node or JavaScript |
| Data analysis | pandas |
Use Python list comprehensions |
| Database | psycopg2, pymongo |
Use n8n database nodes |
| Web scraping | beautifulsoup4 |
Use HTML Extract node |
| Excel | openpyxl |
Use Spreadsheet File node |
| Image processing | pillow |
Use external API or node |
Available Standard Library Modules
# ✅ THESE WORK - Standard library only
import json # JSON parsing
import datetime # Date/time operations
import re # Regular expressions
import base64 # Base64 encoding
import hashlib # Hashing (MD5, SHA256)
import urllib.parse # URL parsing and encoding
import math # Math functions
import random # Random numbers
import statistics # Statistical functions
import collections # defaultdict, Counter, etc.
Error #2: Empty Code / Missing Return
Frequency: Common across all Code nodes
What it is: Code node has no code or no return statement.
The Problem
# ❌ WRONG: Empty code
# (nothing here)
# ❌ WRONG: Code but no return
items = _input.all()
processed = [item for item in items if item["json"].get("active")]
# Forgot to return!
# ❌ WRONG: Return in wrong scope
if _input.all():
return [{"json": {"result": "success"}}]
# Return is inside if block - may not execute!
The Solution
# ✅ CORRECT: Always return
all_items = _input.all()
if not all_items:
# Return empty array or error
return [{"json": {"error": "No items"}}]
# Process items
processed = [item for item in all_items if item["json"].get("active")]
# Always return at the end
return processed if processed else [{"json": {"message": "No active items"}}]
Best Practice
# ✅ GOOD: Return at end of function (unconditional)
def process_items():
items = _input.all()
if not items:
return [{"json": {"error": "Empty input"}}]
# Process
result = []
for item in items:
result.append({"json": item["json"]})
return result
# Call function and return result
return process_items()
Error #3: KeyError
Frequency: Very common in Python Code nodes
What it is: Accessing dictionary key that doesn't exist.
The Problem
# ❌ WRONG: Direct key access
item = _input.first()["json"]
name = item["name"] # KeyError if "name" doesn't exist!
email = item["email"] # KeyError if "email" doesn't exist!
age = item["age"] # KeyError if "age" doesn't exist!
return [{
"json": {
"name": name,
"email": email,
"age": age
}
}]
Error Message
KeyError: 'name'
The Solution
# ✅ CORRECT: Use .get() with defaults
item = _input.first()["json"]
name = item.get("name", "Unknown")
email = item.get("email", "no-email@example.com")
age = item.get("age", 0)
return [{
"json": {
"name": name,
"email": email,
"age": age
}
}]
Nested Dictionary Access
# ❌ WRONG: Nested key access
webhook = _input.first()["json"]
name = webhook["body"]["user"]["name"] # Multiple KeyErrors possible!
# ✅ CORRECT: Safe nested access
webhook = _input.first()["json"]
body = webhook.get("body", {})
user = body.get("user", {})
name = user.get("name", "Unknown")
# ✅ ALSO CORRECT: Chained .get()
name = (
webhook
.get("body", {})
.get("user", {})
.get("name", "Unknown")
)
return [{"json": {"name": name}}]
Webhook Body Access (Critical!)
# ❌ WRONG: Forgetting webhook data is under "body"
webhook = _input.first()["json"]
name = webhook["name"] # KeyError!
email = webhook["email"] # KeyError!
# ✅ CORRECT: Access via ["body"]
webhook = _input.first()["json"]
body = webhook.get("body", {})
name = body.get("name", "Unknown")
email = body.get("email", "no-email")
return [{
"json": {
"name": name,
"email": email
}
}]
Error #4: IndexError
Frequency: Common when processing arrays/lists
What it is: Accessing list index that doesn't exist.
The Problem
# ❌ WRONG: Assuming items exist
all_items = _input.all()
first_item = all_items[0] # IndexError if list is empty!
second_item = all_items[1] # IndexError if only 1 item!
return [{
"json": {
"first": first_item["json"],
"second": second_item["json"]
}
}]
Error Message
IndexError: list index out of range
The Solution
# ✅ CORRECT: Check length first
all_items = _input.all()
if len(all_items) >= 2:
first_item = all_items[0]["json"]
second_item = all_items[1]["json"]
return [{
"json": {
"first": first_item,
"second": second_item
}
}]
else:
return [{
"json": {
"error": f"Expected 2+ items, got {len(all_items)}"
}
}]
Safe First Item Access
# ✅ CORRECT: Use _input.first() instead of [0]
# This is safer than manual indexing
first_item = _input.first()["json"]
return [{"json": first_item}]
# ✅ ALSO CORRECT: Check before accessing
all_items = _input.all()
if all_items:
first_item = all_items[0]["json"]
else:
first_item = {}
return [{"json": first_item}]
Slice Instead of Index
# ✅ CORRECT: Use slicing (never raises IndexError)
all_items = _input.all()
# Get first 5 items (won't fail if fewer than 5)
first_five = all_items[:5]
# Get items after first (won't fail if empty)
rest = all_items[1:]
return [{"json": item["json"]} for item in first_five]
Error #5: Incorrect Return Format
Frequency: Common for new users
What it is: Returning data in wrong format (n8n expects array of objects with "json" key).
The Problem
# ❌ WRONG: Returning plain dictionary
return {"name": "Alice", "age": 30}
# ❌ WRONG: Returning array without "json" wrapper
return [{"name": "Alice"}, {"name": "Bob"}]
# ❌ WRONG: Returning None
return None
# ❌ WRONG: Returning string
return "success"
# ❌ WRONG: Returning single item (not array)
return {"json": {"name": "Alice"}}
The Solution
# ✅ CORRECT: Array of objects with "json" key
return [{"json": {"name": "Alice", "age": 30}}]
# ✅ CORRECT: Multiple items
return [
{"json": {"name": "Alice"}},
{"json": {"name": "Bob"}}
]
# ✅ CORRECT: Transform items
all_items = _input.all()
return [
{"json": item["json"]}
for item in all_items
]
# ✅ CORRECT: Empty array (valid)
return []
# ✅ CORRECT: Single item still needs array wrapper
return [{"json": {"result": "success"}}]
Common Scenarios
Scenario 1: Aggregation (Return Single Result)
# Calculate total
all_items = _input.all()
total = sum(item["json"].get("amount", 0) for item in all_items)
# ✅ CORRECT: Wrap in array with "json"
return [{
"json": {
"total": total,
"count": len(all_items)
}
}]
Scenario 2: Filtering (Return Multiple Results)
# Filter active items
all_items = _input.all()
active = [item for item in all_items if item["json"].get("active")]
# ✅ CORRECT: Already in correct format
return active
# ✅ ALSO CORRECT: If transforming
return [
{"json": {**item["json"], "filtered": True}}
for item in active
]
Scenario 3: No Results
# ✅ CORRECT: Return empty array
return []
# ✅ ALSO CORRECT: Return error message
return [{"json": {"error": "No results found"}}]
Bonus Error: AttributeError
What it is: Using _input.item in wrong mode.
The Problem
# ❌ WRONG: Using _input.item in "All Items" mode
current = _input.item # None in "All Items" mode
data = current["json"] # AttributeError: 'NoneType' object has no attribute '__getitem__'
The Solution
# ✅ CORRECT: Check mode or use appropriate method
# In "All Items" mode, use:
all_items = _input.all()
# In "Each Item" mode, use:
current_item = _input.item
# ✅ SAFE: Check if item exists
current = _input.item
if current:
data = current["json"]
return [{"json": data}]
else:
# Running in "All Items" mode
return _input.all()
Error Prevention Checklist
Before running your Python Code node, verify:
- No external imports: Only standard library (json, datetime, re, etc.)
- Code returns data: Every code path ends with
return - Correct format: Returns
[{"json": {...}}](array with "json" key) - Safe dictionary access: Uses
.get()instead of[]for dictionaries - Safe list access: Checks length before indexing or uses slicing
- Webhook body access: Accesses webhook data via
_json["body"] - No None returns: Returns empty array
[]instead ofNone - Mode awareness: Uses
_input.all(),_input.first(), or_input.itemappropriately
Quick Fix Reference
| Error | Quick Fix |
|---|---|
ModuleNotFoundError |
Use JavaScript or HTTP Request node instead |
KeyError: 'field' |
Change data["field"] to data.get("field", default) |
IndexError: list index out of range |
Check if len(items) > 0: before items[0] |
| Empty output | Add return [{"json": {...}}] at end |
AttributeError: 'NoneType' |
Check mode setting or verify _input.item exists |
| Wrong format error | Wrap result: return [{"json": result}] |
| Webhook KeyError | Access via _json.get("body", {}) |
Testing Your Code
Test Pattern 1: Handle Empty Input
# ✅ Always test with empty input
all_items = _input.all()
if not all_items:
return [{"json": {"message": "No items to process"}}]
# Continue with processing
# ...
Test Pattern 2: Test with Missing Fields
# ✅ Use .get() with defaults
item = _input.first()["json"]
# These won't fail even if fields missing
name = item.get("name", "Unknown")
email = item.get("email", "no-email")
age = item.get("age", 0)
return [{"json": {"name": name, "email": email, "age": age}}]
Test Pattern 3: Test Both Modes
# ✅ Code that works in both modes
try:
# Try "Each Item" mode first
current = _input.item
if current:
return [{"json": current["json"]}]
except:
pass
# Fall back to "All Items" mode
all_items = _input.all()
return all_items if all_items else [{"json": {"message": "No data"}}]
Summary
Top 5 Errors to Avoid:
- ModuleNotFoundError - Use JavaScript or n8n nodes instead
- Missing return - Always end with
return [{"json": {...}}] - KeyError - Use
.get()for dictionary access - IndexError - Check length before indexing
- Wrong format - Return
[{"json": {...}}], not plain objects
Golden Rules:
- NO external libraries (use JavaScript instead)
- ALWAYS use
.get()for dictionaries - ALWAYS return
[{"json": {...}}]format - CHECK lengths before list access
- ACCESS webhook data via
["body"]
Remember:
- JavaScript is recommended for 95% of use cases
- Python has limitations (no requests, pandas, numpy)
- Use n8n nodes for complex operations
See Also:
- SKILL.md - Python Code overview
- DATA_ACCESS.md - Data access patterns
- STANDARD_LIBRARY.md - Available modules
- COMMON_PATTERNS.md - Production patterns