diff --git a/src/services/n8n-validation.ts b/src/services/n8n-validation.ts index a94c225..c955214 100644 --- a/src/services/n8n-validation.ts +++ b/src/services/n8n-validation.ts @@ -139,18 +139,44 @@ export function cleanWorkflowForUpdate(workflow: Workflow): Partial { // PROBLEM: // - Some versions reject updates with settings properties (community forum reports) // - Cloud versions REQUIRE settings property to be present (n8n.estyl.team) - // - Properties like callerPolicy and executionOrder cause "additional properties" errors + // - Properties like callerPolicy cause "additional properties" errors // // SOLUTION: - // - ALWAYS set settings to empty object {}, regardless of whether it exists + // - Filter settings to only include whitelisted properties (OpenAPI spec) + // - If no settings provided, use empty object {} for safety // - Empty object satisfies "required property" validation (cloud API) - // - Empty object has no "additional properties" to trigger errors (self-hosted) - // - n8n API interprets empty settings as "no changes" and preserves existing settings + // - Whitelisted properties prevent "additional properties" errors // // References: // - https://community.n8n.io/t/api-workflow-update-endpoint-doesnt-support-setting-callerpolicy/161916 + // - OpenAPI spec: workflowSettings schema // - Tested on n8n.estyl.team (cloud) and localhost (self-hosted) - cleanedWorkflow.settings = {}; + + // Whitelisted settings properties from n8n OpenAPI spec + const safeSettingsProperties = [ + 'saveExecutionProgress', + 'saveManualExecutions', + 'saveDataErrorExecution', + 'saveDataSuccessExecution', + 'executionTimeout', + 'errorWorkflow', + 'timezone', + 'executionOrder' + ]; + + if (cleanedWorkflow.settings && typeof cleanedWorkflow.settings === 'object') { + // Filter to only safe properties + const filteredSettings: any = {}; + for (const key of safeSettingsProperties) { + if (key in cleanedWorkflow.settings) { + filteredSettings[key] = (cleanedWorkflow.settings as any)[key]; + } + } + cleanedWorkflow.settings = filteredSettings; + } else { + // No settings provided - use empty object for safety + cleanedWorkflow.settings = {}; + } return cleanedWorkflow; } diff --git a/tests/integration/n8n-api/workflows/update-workflow.test.ts b/tests/integration/n8n-api/workflows/update-workflow.test.ts index 7d72c58..62825ab 100644 --- a/tests/integration/n8n-api/workflows/update-workflow.test.ts +++ b/tests/integration/n8n-api/workflows/update-workflow.test.ts @@ -166,16 +166,17 @@ describe('Integration: handleUpdateWorkflow', () => { if (!created.id) throw new Error('Workflow ID is missing'); context.trackWorkflow(created.id); - // Fetch current workflow to get nodes (required by n8n API) + // Fetch current workflow to get required fields (n8n API requirement) const current = await client.getWorkflow(created.id); // Remove connections (disconnect nodes) const response = await handleUpdateWorkflow( { id: created.id, - name: current.name, // Required by n8n API - nodes: current.nodes, // Required by n8n API - connections: {} + name: current.name, // Required by n8n API + nodes: current.nodes, // Required by n8n API + connections: {}, + settings: current.settings // Required by n8n API }, mcpContext );