Skip to main content

When to Use

When a webhook returns {"message": "Error in workflow"} or {"code": 0, "message": "No item to return was found"} and you need to diagnose without opening the n8n UI. This method is faster and produces an auditable trace.

Prerequisites

source /home/devarno/code/workspace/so1-io/platform-tools/so1-control-plane-api/.env.local
# Provides: $N8N_API_KEY, $GITHUB_TOKEN

Step 1: Get the Failed Execution

curl -s -H "X-N8N-API-KEY: $N8N_API_KEY" \
  "https://hab.so1.io/api/v1/executions?workflowId=WORKFLOW_ID&limit=1&includeData=true" | \
  python3 -c "
import sys, json
e = json.load(sys.stdin).get('data', json.load(sys.stdin))[0]
print(f'ID:     {e[\"id\"]}')
print(f'Status: {e[\"status\"]}')
print(f'Nodes:  {list(e[\"data\"][\"resultData\"][\"runData\"].keys())}')
error = e['data']['resultData'].get('error', {})
if error:
    print(f'Error:  {error.get(\"message\", \"\")}')
    print(f'Node:   {error.get(\"node\", {}).get(\"name\", \"\")}')
"
What to look for:
  • Which nodes executed (and which didn’t — the gap reveals where it stopped)
  • The error message and which node threw it
  • Whether status is error (crashed) or success (completed but returned empty)

Step 2: Trace Data Flow Through Nodes

curl -s -H "X-N8N-API-KEY: $N8N_API_KEY" \
  "https://hab.so1.io/api/v1/executions?workflowId=WORKFLOW_ID&limit=1&includeData=true" | \
  python3 -c "
import sys, json
e = json.load(sys.stdin).get('data', json.load(sys.stdin))[0]
run_data = e['data']['resultData']['runData']
for node_name, runs in run_data.items():
    for ri, run in enumerate(runs):
        main = run.get('data', {}).get('main', [[]])
        items = sum(len(b) for b in main if b)
        branches = [i for i,b in enumerate(main) if b]
        marker = ' ⚠️' if len(runs) > 1 else ''
        err = ' ❌ ' + run['error']['message'][:80] if run.get('error') else ''
        print(f'[{node_name}]: {items} items b{branches}{marker}{err}')
"
Red flags:
  • ⚠️ = node ran multiple times (duplication bug)
  • Items dropping to 0 between nodes (data lost)
  • Wrong branch active (e.g., b[1] when b[0] expected)
  • Node not in the list at all (never triggered)

Step 3: Inspect a Specific Node’s Output

NODE_NAME="🧩 Assemble Inspection Context"
curl -s -H "X-N8N-API-KEY: $N8N_API_KEY" \
  "https://hab.so1.io/api/v1/executions?workflowId=WORKFLOW_ID&limit=1&includeData=true" | \
  python3 -c "
import sys, json
e = json.load(sys.stdin).get('data', json.load(sys.stdin))[0]
runs = e['data']['resultData']['runData'].get('$NODE_NAME', [])
for run in runs:
    for branch in run.get('data', {}).get('main', [[]]):
        if branch:
            for item in branch[:3]:
                j = item.get('json', {})
                print(json.dumps({k: v for k,v in j.items()
                    if not isinstance(v, (list,dict)) or len(str(v)) < 200}, indent=2))
"

Step 4: Check Workflow Connections

curl -s -H "X-N8N-API-KEY: $N8N_API_KEY" \
  "https://hab.so1.io/api/v1/workflows/WORKFLOW_ID" | \
  python3 -c "
import sys, json
wf = json.load(sys.stdin)
for src, targets in sorted(wf['connections'].items()):
    for otype, branches in targets.items():
        for bi, branch in enumerate(branches):
            for conn in branch:
                print(f'{src} --[b{bi}]--> {conn[\"node\"]}')
"

Step 5: Read a Node’s Code or Config

curl -s -H "X-N8N-API-KEY: $N8N_API_KEY" \
  "https://hab.so1.io/api/v1/workflows/WORKFLOW_ID" | \
  python3 -c "
import sys, json
wf = json.load(sys.stdin)
for n in wf['nodes']:
    if 'TARGET_NAME' in n['name']:
        print(f'Type: {n[\"type\"]}')
        print(f'Params: {json.dumps(n.get(\"parameters\",{}), indent=2)[:3000]}')
"

Common n8n Bug Patterns

SymptomRoot CauseFix
Node runs 2-3xParallel fan-out into single nodeChain sequentially or add Merge node
"Wrong type" on If nodetypeValidation: "strict" + null/empty coercionChange condition to number comparison (e.g., .length > 0)
"No item to return"Merge node with output: "empty"Have downstream Code node use $('NodeName') references
"Node X hasn't been executed"$() reference to node that hasn’t finished yetChain nodes sequentially
"Code doesn't return a single object"Array return in runOnceForEachItem modeReturn {json: ...} not [{json: ...}]
SplitInBatches skips loop bodyBatch size not explicitly set in v3Set Batch Size = 1 in Options
Context fields (repo, branch) emptyUpstream node output replaces config dataUse $('Config Node').first().json to reference config directly
HTTP 404 crashes pipelineOn Error: "Stop Workflow" on optional fetchSet to "Continue (Using Regular Output)"

Token Usage Tracking

After each execution involving LLM calls, extract the cost:
curl -s -H "X-N8N-API-KEY: $N8N_API_KEY" \
  "https://hab.so1.io/api/v1/executions?workflowId=WORKFLOW_ID&limit=1&includeData=true" | \
  python3 -c "
import sys, json
e = json.load(sys.stdin).get('data', json.load(sys.stdin))[0]
run_data = e['data']['resultData']['runData']
for name, runs in run_data.items():
    for run in runs:
        for branch in run.get('data',{}).get('main',[[]]):
            if branch:
                for item in branch:
                    usage = item.get('json',{}).get('usage',{})
                    if usage.get('input_tokens'):
                        inp = usage['input_tokens']
                        out = usage['output_tokens']
                        # Sonnet: \$3/1M in, \$15/1M out
                        cost = (inp/1e6)*3 + (out/1e6)*15
                        print(f'[{name}] {inp} in + {out} out = \${cost:.4f}')
"