fix(p0): remove incorrect node type normalization before n8n API calls

## Bug Description
handleCreateWorkflow and handleUpdateFullWorkflow were incorrectly
normalizing node types from FULL form (n8n-nodes-base.webhook) to
SHORT form (nodes-base.webhook) before validation and API calls.

This caused 100% failure rate for workflow creation because:
- n8n API requires FULL form (n8n-nodes-base.*)
- Database stores SHORT form (nodes-base.*)
- NodeTypeNormalizer converts TO SHORT form (for database)
- But was being used BEFORE API calls (incorrect)

## Root Cause
NodeTypeNormalizer was designed for database lookups but was
incorrectly applied to API operations. The method name
`normalizeToFullForm()` is misleading - it actually normalizes
TO SHORT form.

## Changes
1. handlers-n8n-manager.ts:
   - Removed NodeTypeNormalizer.normalizeWorkflowNodeTypes() from
     handleCreateWorkflow (line 288)
   - Removed normalization from handleUpdateFullWorkflow (line 544-557)
   - Added proactive SHORT form detection with helpful errors
   - Added comments explaining n8n API expects FULL form

2. node-type-normalizer.ts:
   - Added prominent WARNING about not using before API calls
   - Added examples showing CORRECT vs INCORRECT usage
   - Clarified this is FOR DATABASE OPERATIONS ONLY

3. handlers-n8n-manager.test.ts:
   - Fixed test to expect FULL form (not SHORT) sent to API
   - Removed incorrect expectedNormalizedInput assertion

4. NEW: workflow-creation-node-type-format.test.ts:
   - 7 integration tests with real validation (unmocked)
   - Tests FULL form acceptance, SHORT form rejection
   - Tests real-world workflows (webhook, schedule trigger)
   - Regression test to prevent bug reintroduction

## Verification
Before fix:
 Manual Trigger → Set: FAILED
 Webhook → HTTP Request: FAILED
Failure rate: 100%

After fix:
 Manual Trigger → Set: SUCCESS (ID: kTAaDZwdpzj8gqzM)
 Webhook → HTTP Request: SUCCESS (ID: aPtQUb54uuHIqX52)
 All 39 tests passing (32 unit + 7 integration)
Success rate: 100%

## Impact
- Fixes: Complete blocking bug preventing all workflow creation
- Risk: Zero (removing buggy behavior)
- Breaking: None (external API unchanged)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-10-03 11:43:49 +02:00
parent 5bf1bc46e9
commit d875ac1e0c
7 changed files with 578 additions and 162 deletions

View File

@@ -284,14 +284,37 @@ export async function handleCreateWorkflow(args: unknown, context?: InstanceCont
const client = ensureApiConfigured(context);
const input = createWorkflowSchema.parse(args);
// Normalize all node types before validation
const normalizedInput = NodeTypeNormalizer.normalizeWorkflowNodeTypes(input);
// Proactively detect SHORT form node types (common mistake)
const shortFormErrors: string[] = [];
input.nodes?.forEach((node: any, index: number) => {
if (node.type?.startsWith('nodes-base.') || node.type?.startsWith('nodes-langchain.')) {
const fullForm = node.type.startsWith('nodes-base.')
? node.type.replace('nodes-base.', 'n8n-nodes-base.')
: node.type.replace('nodes-langchain.', '@n8n/n8n-nodes-langchain.');
shortFormErrors.push(
`Node ${index} ("${node.name}") uses SHORT form "${node.type}". ` +
`The n8n API requires FULL form. Change to "${fullForm}"`
);
}
});
// Validate workflow structure
const errors = validateWorkflowStructure(normalizedInput);
if (shortFormErrors.length > 0) {
telemetry.trackWorkflowCreation(input, false);
return {
success: false,
error: 'Node type format error: n8n API requires FULL form node types',
details: {
errors: shortFormErrors,
hint: 'Use n8n-nodes-base.* instead of nodes-base.* for standard nodes'
}
};
}
// Validate workflow structure (n8n API expects FULL form: n8n-nodes-base.*)
const errors = validateWorkflowStructure(input);
if (errors.length > 0) {
// Track validation failure
telemetry.trackWorkflowCreation(normalizedInput, false);
telemetry.trackWorkflowCreation(input, false);
return {
success: false,
@@ -300,8 +323,8 @@ export async function handleCreateWorkflow(args: unknown, context?: InstanceCont
};
}
// Create workflow with normalized node types
const workflow = await client.createWorkflow(normalizedInput);
// Create workflow (n8n API expects node types in FULL form)
const workflow = await client.createWorkflow(input);
// Track successful workflow creation
telemetry.trackWorkflowCreation(workflow, true);
@@ -540,10 +563,8 @@ export async function handleUpdateWorkflow(args: unknown, context?: InstanceCont
};
}
// Normalize all node types before validation
const normalizedWorkflow = NodeTypeNormalizer.normalizeWorkflowNodeTypes(fullWorkflow);
const errors = validateWorkflowStructure(normalizedWorkflow);
// Validate workflow structure (n8n API expects FULL form: n8n-nodes-base.*)
const errors = validateWorkflowStructure(fullWorkflow);
if (errors.length > 0) {
return {
success: false,
@@ -551,11 +572,6 @@ export async function handleUpdateWorkflow(args: unknown, context?: InstanceCont
details: { errors }
};
}
// Update updateData with normalized nodes if they were modified
if (updateData.nodes) {
updateData.nodes = normalizedWorkflow.nodes;
}
}
// Update workflow