Files
gh-czlonkowski-n8n-skills/skills/n8n-code-python/ERROR_PATTERNS.md
2025-11-29 18:17:17 +08:00

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:

  1. ModuleNotFoundError - Trying to import external libraries (Python-specific)
  2. Empty Code / Missing Return - No code or return statement
  3. KeyError - Dictionary access without .get()
  4. IndexError - List access without bounds checking
  5. 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 of None
  • Mode awareness: Uses _input.all(), _input.first(), or _input.item appropriately

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:

  1. ModuleNotFoundError - Use JavaScript or n8n nodes instead
  2. Missing return - Always end with return [{"json": {...}}]
  3. KeyError - Use .get() for dictionary access
  4. IndexError - Check length before indexing
  5. 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: