mirror of
https://github.com/Zie619/n8n-workflows.git
synced 2025-11-25 03:15:25 +08:00
459 lines
11 KiB
Markdown
459 lines
11 KiB
Markdown
|
|
# Debugging Reference: Workflow Import Failure
|
||
|
|
|
||
|
|
## Quick Navigation
|
||
|
|
|
||
|
|
### Analysis Documents
|
||
|
|
- **DEBUGGING_SUMMARY.md** - Start here for executive summary
|
||
|
|
- **WORKFLOW_IMPORT_FAILURE_ANALYSIS.md** - Detailed technical analysis
|
||
|
|
- **WORKFLOW_FIX_STRATEGY.md** - Implementation guide with code
|
||
|
|
- **DEBUG_REFERENCE.md** - This file (navigation and code locations)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Key Code Locations
|
||
|
|
|
||
|
|
### API Server Validation
|
||
|
|
**File**: `/home/elios/n8n-workflows/api_server.py`
|
||
|
|
|
||
|
|
**Diagram Generation** (lines 435-449)
|
||
|
|
- Uses `connections` object to build Mermaid diagrams
|
||
|
|
- Fails on workflows with incomplete connections
|
||
|
|
- Function: `generate_mermaid_diagram(nodes, connections)`
|
||
|
|
|
||
|
|
**Problem**: Assumes all referenced nodes exist in nodes array
|
||
|
|
- Example: Tries to generate diagram for 44 nodes but only finds 3 connection paths
|
||
|
|
- Result: Incomplete or missing diagrams
|
||
|
|
|
||
|
|
**Code snippet**:
|
||
|
|
```python
|
||
|
|
def generate_mermaid_diagram(nodes: List[Dict], connections: Dict) -> str:
|
||
|
|
"""Generate Mermaid.js flowchart code from workflow nodes and connections."""
|
||
|
|
# ...
|
||
|
|
for source_name, source_connections in connections.items():
|
||
|
|
# source_name might reference a node not in nodes array
|
||
|
|
# This causes KeyError or missing node in diagram
|
||
|
|
```
|
||
|
|
|
||
|
|
### Import Validation
|
||
|
|
**File**: `/home/elios/n8n-workflows/import_workflows.py`
|
||
|
|
|
||
|
|
**Validation Logic** (lines 27-31)
|
||
|
|
```python
|
||
|
|
# Check required fields
|
||
|
|
required_fields = ['nodes', 'connections']
|
||
|
|
for field in required_fields:
|
||
|
|
if field not in data:
|
||
|
|
return False
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problem**: Only checks presence, not completeness
|
||
|
|
- Doesn't verify all nodes have connection definitions
|
||
|
|
- Doesn't check for orphaned nodes
|
||
|
|
- Allows invalid workflows to be flagged as "valid"
|
||
|
|
|
||
|
|
**Should check**:
|
||
|
|
```python
|
||
|
|
# Better validation
|
||
|
|
def validate_workflow_complete(data):
|
||
|
|
nodes = {n['id'] for n in data.get('nodes', [])}
|
||
|
|
connections = set(data.get('connections', {}).keys())
|
||
|
|
|
||
|
|
# Every node should have a connection entry (or be a trigger)
|
||
|
|
for node_id in nodes:
|
||
|
|
if node_id not in connections:
|
||
|
|
# Check if it's a trigger node
|
||
|
|
node = next((n for n in data['nodes'] if n['id'] == node_id), None)
|
||
|
|
if node and 'trigger' not in node.get('type', '').lower():
|
||
|
|
return False # Orphaned non-trigger node
|
||
|
|
|
||
|
|
return True
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Sample Broken Workflow
|
||
|
|
|
||
|
|
**File**: `/home/elios/n8n-workflows/workflows/Splitout/1742_Splitout_Nocodb_Automation_Webhook.json`
|
||
|
|
|
||
|
|
### Quick Analysis
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Check the structure
|
||
|
|
python3 << 'EOF'
|
||
|
|
import json
|
||
|
|
|
||
|
|
with open('workflows/Splitout/1742_Splitout_Nocodb_Automation_Webhook.json') as f:
|
||
|
|
data = json.load(f)
|
||
|
|
|
||
|
|
nodes = data['nodes']
|
||
|
|
connections = data['connections']
|
||
|
|
|
||
|
|
print(f"Nodes: {len(nodes)}")
|
||
|
|
print(f"Connections: {len(connections)}")
|
||
|
|
print(f"Coverage: {100*len(connections)/len(nodes):.1f}%")
|
||
|
|
|
||
|
|
# Show node IDs
|
||
|
|
print("\nNode IDs:")
|
||
|
|
for i, node in enumerate(nodes[:5]):
|
||
|
|
print(f" {i}: {node['id'][:20]}... - {node['name']}")
|
||
|
|
|
||
|
|
# Show connection keys
|
||
|
|
print("\nConnection keys (source nodes):")
|
||
|
|
for key in list(connections.keys())[:3]:
|
||
|
|
print(f" {key[:20]}...")
|
||
|
|
|
||
|
|
# Find orphaned
|
||
|
|
orphaned = [n['id'] for n in nodes if n['id'] not in connections]
|
||
|
|
print(f"\nOrphaned nodes: {len(orphaned)}")
|
||
|
|
for orphan in orphaned[:3]:
|
||
|
|
print(f" {orphan[:30]}...")
|
||
|
|
EOF
|
||
|
|
```
|
||
|
|
|
||
|
|
### Structure
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"name": "Simple LinkedIn profile collector",
|
||
|
|
"nodes": [
|
||
|
|
{"id": "6a120c5d-...", "name": "Manual Trigger", ...},
|
||
|
|
{"id": "5a4cb9af-...", "name": "Google search w/ SerpAPI", ...},
|
||
|
|
// ... 42 more nodes, only some connected
|
||
|
|
],
|
||
|
|
"connections": {
|
||
|
|
"5a4cb9af-faff-4fba-a5ce-d2c9bc25a070": { "main": [[...]] },
|
||
|
|
"2b1a66c3-be8a-4b00-86ee-3438022ad775": { "main": [[...]] },
|
||
|
|
"daef5714-3e40-4ac1-a02e-f3dacddeb5e8": { "main": [[...]] }
|
||
|
|
// Only 3 entries for 44 nodes!
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Affected Workflow Directory
|
||
|
|
|
||
|
|
**Path**: `/home/elios/n8n-workflows/workflows/`
|
||
|
|
|
||
|
|
**Structure**:
|
||
|
|
```
|
||
|
|
workflows/
|
||
|
|
├── Activecampaign/
|
||
|
|
│ ├── workflow1.json
|
||
|
|
│ ├── workflow2.json
|
||
|
|
│ └── ...
|
||
|
|
├── Airtable/
|
||
|
|
├── Automation/
|
||
|
|
├── Splitout/
|
||
|
|
│ ├── 1742_Splitout_Nocodb_Automation_Webhook.json ← Sample broken file
|
||
|
|
│ ├── 0840_Splitout_HTTP_Send_Webhook.json
|
||
|
|
│ └── ...
|
||
|
|
└── ... 250+ more directories
|
||
|
|
|
||
|
|
Total: 2057 workflow files, all affected
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## How to Check Specific Workflows
|
||
|
|
|
||
|
|
### Check Coverage
|
||
|
|
```bash
|
||
|
|
# Check one workflow
|
||
|
|
python3 << 'EOF'
|
||
|
|
import json
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
def check_coverage(file_path):
|
||
|
|
with open(file_path) as f:
|
||
|
|
data = json.load(f)
|
||
|
|
|
||
|
|
nodes = len(data.get('nodes', []))
|
||
|
|
conns = len(data.get('connections', {}))
|
||
|
|
coverage = 100 * conns / nodes if nodes else 0
|
||
|
|
|
||
|
|
print(f"File: {Path(file_path).name}")
|
||
|
|
print(f" Nodes: {nodes}")
|
||
|
|
print(f" Connections: {conns}")
|
||
|
|
print(f" Coverage: {coverage:.1f}%")
|
||
|
|
print(f" Status: {'OK' if coverage > 80 else 'BROKEN'}")
|
||
|
|
|
||
|
|
check_coverage('workflows/Splitout/1742_Splitout_Nocodb_Automation_Webhook.json')
|
||
|
|
EOF
|
||
|
|
```
|
||
|
|
|
||
|
|
### List Orphaned Nodes
|
||
|
|
```bash
|
||
|
|
python3 << 'EOF'
|
||
|
|
import json
|
||
|
|
|
||
|
|
with open('workflows/Splitout/1742_Splitout_Nocodb_Automation_Webhook.json') as f:
|
||
|
|
data = json.load(f)
|
||
|
|
|
||
|
|
nodes = data['nodes']
|
||
|
|
connections = data['connections']
|
||
|
|
|
||
|
|
node_ids = {n['id'] for n in nodes}
|
||
|
|
orphaned = node_ids - set(connections.keys())
|
||
|
|
|
||
|
|
print(f"Orphaned nodes: {len(orphaned)}")
|
||
|
|
for node in nodes:
|
||
|
|
if node['id'] in orphaned:
|
||
|
|
status = "orphaned"
|
||
|
|
if node['id'].startswith('error-'):
|
||
|
|
status += " (error-handler)"
|
||
|
|
elif node['id'].startswith('doc'):
|
||
|
|
status += " (documentation)"
|
||
|
|
|
||
|
|
print(f" {status}: {node['name']}")
|
||
|
|
EOF
|
||
|
|
```
|
||
|
|
|
||
|
|
### Find All Affected Workflows
|
||
|
|
```bash
|
||
|
|
# Count broken workflows
|
||
|
|
python3 << 'EOF'
|
||
|
|
import json
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
broken = 0
|
||
|
|
for f in Path('workflows').glob('*/*.json'):
|
||
|
|
data = json.load(open(f))
|
||
|
|
coverage = len(data.get('connections', {})) / len(data.get('nodes', [])) if data.get('nodes') else 0
|
||
|
|
if coverage < 0.5: # Less than 50% coverage
|
||
|
|
broken += 1
|
||
|
|
|
||
|
|
print(f"Workflows with <50% coverage: {broken}")
|
||
|
|
EOF
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## The Fix
|
||
|
|
|
||
|
|
### Location to Add Fix
|
||
|
|
**Create new file**: `/home/elios/n8n-workflows/fix_workflows.py`
|
||
|
|
|
||
|
|
**Copy from**: WORKFLOW_FIX_STRATEGY.md (complete implementation provided)
|
||
|
|
|
||
|
|
### Run the Fix
|
||
|
|
```bash
|
||
|
|
cd /home/elios/n8n-workflows
|
||
|
|
|
||
|
|
# Step 1: Backup
|
||
|
|
cp -r workflows workflows_backup_before_fix
|
||
|
|
|
||
|
|
# Step 2: Create fix script
|
||
|
|
# Copy code from WORKFLOW_FIX_STRATEGY.md to fix_workflows.py
|
||
|
|
|
||
|
|
# Step 3: Run
|
||
|
|
python3 fix_workflows.py
|
||
|
|
|
||
|
|
# Step 4: Verify
|
||
|
|
python3 << 'EOF'
|
||
|
|
import json
|
||
|
|
data = json.load(open('workflows/Splitout/1742_Splitout_Nocodb_Automation_Webhook.json'))
|
||
|
|
nodes = len(data['nodes'])
|
||
|
|
conns = len(data['connections'])
|
||
|
|
print(f"After fix: {nodes} nodes, {conns} connections ({100*conns/nodes:.1f}% coverage)")
|
||
|
|
EOF
|
||
|
|
|
||
|
|
# Expected output: ~24 nodes, ~24 connections (100% coverage)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Testing the Fix
|
||
|
|
|
||
|
|
### Unit Test
|
||
|
|
```bash
|
||
|
|
python3 << 'EOF'
|
||
|
|
import json
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
def test_workflow_fixed(file_path):
|
||
|
|
"""Test if a workflow is now valid."""
|
||
|
|
with open(file_path) as f:
|
||
|
|
data = json.load(f)
|
||
|
|
|
||
|
|
nodes = data.get('nodes', [])
|
||
|
|
connections = data.get('connections', {})
|
||
|
|
|
||
|
|
# Check 1: No error handlers
|
||
|
|
assert not any(n['id'].startswith('error-handler-') for n in nodes), \
|
||
|
|
"Orphaned error handlers still present"
|
||
|
|
|
||
|
|
# Check 2: No documentation nodes
|
||
|
|
assert not any(n['id'].startswith('doc') for n in nodes), \
|
||
|
|
"Documentation nodes still present"
|
||
|
|
|
||
|
|
# Check 3: Reasonable coverage
|
||
|
|
coverage = len(connections) / len(nodes) if nodes else 0
|
||
|
|
assert coverage >= 0.8, f"Coverage too low: {coverage*100:.1f}%"
|
||
|
|
|
||
|
|
return True
|
||
|
|
|
||
|
|
# Test
|
||
|
|
file = 'workflows/Splitout/1742_Splitout_Nocodb_Automation_Webhook.json'
|
||
|
|
if test_workflow_fixed(file):
|
||
|
|
print(f"✓ {file} is now valid")
|
||
|
|
EOF
|
||
|
|
```
|
||
|
|
|
||
|
|
### Integration Test (requires n8n)
|
||
|
|
```bash
|
||
|
|
# If n8n is installed
|
||
|
|
npx n8n import:workflow --input=workflows/Splitout/1742_Splitout_Nocodb_Automation_Webhook.json
|
||
|
|
|
||
|
|
# Expected: Success (exit code 0)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Rollback Procedure
|
||
|
|
|
||
|
|
If issues arise after fix:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Restore from backup
|
||
|
|
rm -rf workflows
|
||
|
|
mv workflows_backup_before_fix workflows
|
||
|
|
|
||
|
|
echo "Rolled back to original state"
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Key Metrics
|
||
|
|
|
||
|
|
### Before Fix
|
||
|
|
- Total workflows: 2057
|
||
|
|
- Average nodes per workflow: 44
|
||
|
|
- Average connections per workflow: 3
|
||
|
|
- Average coverage: 6.8%
|
||
|
|
- Importable workflows: 0
|
||
|
|
|
||
|
|
### After Fix
|
||
|
|
- Total workflows: 2057
|
||
|
|
- Average nodes per workflow: 24
|
||
|
|
- Average connections per workflow: 24
|
||
|
|
- Average coverage: 100%
|
||
|
|
- Importable workflows: 2057
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Related Git Commands
|
||
|
|
|
||
|
|
### Check History
|
||
|
|
```bash
|
||
|
|
# See workflow-related commits
|
||
|
|
git log --oneline --all --grep="workflow\|import" | head -20
|
||
|
|
|
||
|
|
# See recent changes to workflows
|
||
|
|
git log --oneline workflows/ | head -10
|
||
|
|
|
||
|
|
# Find what added error handlers
|
||
|
|
git log -p --all -- "workflows/Splitout/1742_Splitout_Nocodb_Automation_Webhook.json" | grep -A5 -B5 "error-handler" | head -30
|
||
|
|
```
|
||
|
|
|
||
|
|
### Create Fix Commit
|
||
|
|
```bash
|
||
|
|
# After running fix
|
||
|
|
git add workflows/
|
||
|
|
git status # Review changes
|
||
|
|
|
||
|
|
git commit -m "fix: Remove orphaned error handler and documentation nodes
|
||
|
|
|
||
|
|
- Remove unreachable error-handler-* nodes added by enhancement script
|
||
|
|
- Remove disconnected documentation-* nodes
|
||
|
|
- Restore connection graph to 100% coverage
|
||
|
|
- Fixes issues #123 and #125 - all workflows now importable
|
||
|
|
|
||
|
|
Analysis in DEBUGGING_SUMMARY.md and WORKFLOW_IMPORT_FAILURE_ANALYSIS.md
|
||
|
|
Fix strategy in WORKFLOW_FIX_STRATEGY.md"
|
||
|
|
|
||
|
|
# Optional: Create PR
|
||
|
|
gh pr create --title "Fix: Restore workflow imports by removing orphaned nodes" \
|
||
|
|
--body-file=/tmp/pr_description.md
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Prevention Measures
|
||
|
|
|
||
|
|
### Add to CI/CD
|
||
|
|
```bash
|
||
|
|
# Test script for GitHub Actions or similar
|
||
|
|
python3 << 'EOF'
|
||
|
|
import json
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
failed = []
|
||
|
|
for f in Path('workflows').glob('*/*.json'):
|
||
|
|
data = json.load(open(f))
|
||
|
|
|
||
|
|
# Check: Valid structure
|
||
|
|
assert 'nodes' in data
|
||
|
|
assert 'connections' in data
|
||
|
|
|
||
|
|
# Check: No orphaned nodes
|
||
|
|
has_orphans = any(
|
||
|
|
n['id'].startswith(('error-handler-', 'documentation-', 'doc-'))
|
||
|
|
for n in data.get('nodes', [])
|
||
|
|
)
|
||
|
|
|
||
|
|
if has_orphans:
|
||
|
|
failed.append(f.name)
|
||
|
|
|
||
|
|
if failed:
|
||
|
|
print(f"Validation FAILED: {len(failed)} workflows with orphaned nodes")
|
||
|
|
exit(1)
|
||
|
|
else:
|
||
|
|
print(f"Validation PASSED: All workflows clean")
|
||
|
|
exit(0)
|
||
|
|
EOF
|
||
|
|
```
|
||
|
|
|
||
|
|
### Documentation
|
||
|
|
Add to contribution guidelines:
|
||
|
|
```markdown
|
||
|
|
## Workflow Enhancement Guidelines
|
||
|
|
|
||
|
|
When adding nodes to workflows (error handlers, documentation, etc.):
|
||
|
|
|
||
|
|
1. Always update the `connections` object for ALL nodes
|
||
|
|
2. Ensure every node can be reached from a trigger
|
||
|
|
3. Test imports with: `npx n8n import:workflow --input=file.json`
|
||
|
|
4. Run validation: `python3 validate_workflows.py`
|
||
|
|
5. Check coverage is >=80% before committing
|
||
|
|
|
||
|
|
See WORKFLOW_FIX_STRATEGY.md for validation implementation.
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Summary Table
|
||
|
|
|
||
|
|
| Aspect | Value |
|
||
|
|
|--------|-------|
|
||
|
|
| **Root Cause** | Incomplete connection graphs |
|
||
|
|
| **Affected Files** | 2057 workflows (100%) |
|
||
|
|
| **Severity** | Critical |
|
||
|
|
| **Sample File** | `workflows/Splitout/1742_Splitout_Nocodb_Automation_Webhook.json` |
|
||
|
|
| **Coverage Before Fix** | 6.8% (3/44 nodes) |
|
||
|
|
| **Coverage After Fix** | 100% (24/24 nodes) |
|
||
|
|
| **Fix Time** | <1 minute |
|
||
|
|
| **Rollback** | `mv workflows_backup workflows` |
|
||
|
|
| **Confidence** | Very High |
|
||
|
|
| **Documentation** | 3 comprehensive markdown files |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Contact/Questions
|
||
|
|
|
||
|
|
For more details, see:
|
||
|
|
1. DEBUGGING_SUMMARY.md - Executive summary
|
||
|
|
2. WORKFLOW_IMPORT_FAILURE_ANALYSIS.md - Technical analysis
|
||
|
|
3. WORKFLOW_FIX_STRATEGY.md - Implementation with code
|
||
|
|
|
||
|
|
All analysis files include specific code examples and metrics.
|