mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-24 11:23:08 +00:00
fix: resolve empty settings validation error in workflow updates (#431)
Fixed critical bug where n8n_update_partial_workflow failed with "request/body
must NOT have additional properties" error when workflows had no settings or
only non-whitelisted settings.
Root Cause:
- cleanWorkflowForUpdate() was sending empty settings: {} objects
- n8n API rejects empty settings as additional properties violation
- Occurred when workflow had no settings or only filtered properties
Changes:
- Modified cleanWorkflowForUpdate() to delete settings property when empty
- Enhanced applyUpdateSettings() to prevent creating empty settings
- Fixed 3 incorrect tests expecting empty settings objects
- Added 2 comprehensive tests for edge cases
Testing:
- All 75 unit tests in n8n-validation.test.ts passing
- Build and type checking successful
- New tests cover all empty settings scenarios
Related: #431, #248
Related n8n issue: n8n-io/n8n#19587
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
This commit is contained in:
37
CHANGELOG.md
37
CHANGELOG.md
@@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [2.22.21] - 2025-11-20
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
**Fix Empty Settings Object Validation Error (#431)**
|
||||||
|
|
||||||
|
Fixed critical bug where `n8n_update_partial_workflow` tool failed with "request/body must NOT have additional properties" error when workflows had no settings or only non-whitelisted settings properties.
|
||||||
|
|
||||||
|
#### Root Cause
|
||||||
|
- `cleanWorkflowForUpdate()` in `src/services/n8n-validation.ts` was sending empty `settings: {}` objects to the n8n API
|
||||||
|
- n8n API rejects empty settings objects as "additional properties" violation
|
||||||
|
- Issue occurred when:
|
||||||
|
- Workflow had no settings property
|
||||||
|
- Workflow had only non-whitelisted settings (e.g., only `callerPolicy`)
|
||||||
|
|
||||||
|
#### Changes
|
||||||
|
- **Primary Fix**: Modified `cleanWorkflowForUpdate()` to delete `settings` property when empty after filtering
|
||||||
|
- Instead of sending `settings: {}`, the property is now omitted entirely
|
||||||
|
- Added safeguards in lines 193-199 and 201-204
|
||||||
|
- **Secondary Fix**: Enhanced `applyUpdateSettings()` in `workflow-diff-engine.ts` to prevent creating empty settings objects
|
||||||
|
- Only creates/updates settings if operation provides actual properties
|
||||||
|
- **Test Updates**: Fixed 3 incorrect tests that expected empty settings objects
|
||||||
|
- Updated to expect settings property to be omitted instead
|
||||||
|
- Added 2 new comprehensive tests for edge cases
|
||||||
|
|
||||||
|
#### Testing
|
||||||
|
- All 75 unit tests in `n8n-validation.test.ts` passing
|
||||||
|
- New tests cover:
|
||||||
|
- Workflows with no settings → omits property
|
||||||
|
- Workflows with only non-whitelisted settings → omits property
|
||||||
|
- Workflows with mixed settings → keeps only whitelisted properties
|
||||||
|
|
||||||
|
**Related Issues**: #431, #248 (n8n API design limitation)
|
||||||
|
**Related n8n Issue**: n8n-io/n8n#19587 (closed as NOT_PLANNED - MCP server issue)
|
||||||
|
|
||||||
|
Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
|
||||||
|
|
||||||
## [2.22.20] - 2025-11-19
|
## [2.22.20] - 2025-11-19
|
||||||
|
|
||||||
### 🔄 Dependencies
|
### 🔄 Dependencies
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "n8n-mcp",
|
"name": "n8n-mcp",
|
||||||
"version": "2.22.20",
|
"version": "2.22.21",
|
||||||
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
|||||||
@@ -189,10 +189,18 @@ export function cleanWorkflowForUpdate(workflow: Workflow): Partial<Workflow> {
|
|||||||
filteredSettings[key] = (cleanedWorkflow.settings as any)[key];
|
filteredSettings[key] = (cleanedWorkflow.settings as any)[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cleanedWorkflow.settings = filteredSettings;
|
|
||||||
|
// Only include settings if it has properties after filtering
|
||||||
|
// n8n API rejects empty settings objects
|
||||||
|
if (Object.keys(filteredSettings).length > 0) {
|
||||||
|
cleanedWorkflow.settings = filteredSettings;
|
||||||
|
} else {
|
||||||
|
delete cleanedWorkflow.settings;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// No settings provided - use empty object for safety
|
// No settings provided - remove the property entirely
|
||||||
cleanedWorkflow.settings = {};
|
// n8n API rejects empty settings objects
|
||||||
|
delete cleanedWorkflow.settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cleanedWorkflow;
|
return cleanedWorkflow;
|
||||||
|
|||||||
@@ -861,10 +861,14 @@ export class WorkflowDiffEngine {
|
|||||||
|
|
||||||
// Metadata operation appliers
|
// Metadata operation appliers
|
||||||
private applyUpdateSettings(workflow: Workflow, operation: UpdateSettingsOperation): void {
|
private applyUpdateSettings(workflow: Workflow, operation: UpdateSettingsOperation): void {
|
||||||
if (!workflow.settings) {
|
// Only create/update settings if operation provides actual properties
|
||||||
workflow.settings = {};
|
// This prevents creating empty settings objects that would be rejected by n8n API
|
||||||
|
if (operation.settings && Object.keys(operation.settings).length > 0) {
|
||||||
|
if (!workflow.settings) {
|
||||||
|
workflow.settings = {};
|
||||||
|
}
|
||||||
|
Object.assign(workflow.settings, operation.settings);
|
||||||
}
|
}
|
||||||
Object.assign(workflow.settings, operation.settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private applyUpdateName(workflow: Workflow, operation: UpdateNameOperation): void {
|
private applyUpdateName(workflow: Workflow, operation: UpdateNameOperation): void {
|
||||||
|
|||||||
@@ -367,7 +367,7 @@ describe('n8n-validation', () => {
|
|||||||
expect(cleaned.name).toBe('Test Workflow');
|
expect(cleaned.name).toBe('Test Workflow');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add empty settings object for cloud API compatibility', () => {
|
it('should omit settings property when no settings provided (Issue #431)', () => {
|
||||||
const workflow = {
|
const workflow = {
|
||||||
name: 'Test Workflow',
|
name: 'Test Workflow',
|
||||||
nodes: [],
|
nodes: [],
|
||||||
@@ -375,7 +375,7 @@ describe('n8n-validation', () => {
|
|||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
const cleaned = cleanWorkflowForUpdate(workflow);
|
const cleaned = cleanWorkflowForUpdate(workflow);
|
||||||
expect(cleaned.settings).toEqual({});
|
expect(cleaned).not.toHaveProperty('settings');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should filter settings to safe properties to prevent API errors (Issue #248 - final fix)', () => {
|
it('should filter settings to safe properties to prevent API errors (Issue #248 - final fix)', () => {
|
||||||
@@ -467,7 +467,48 @@ describe('n8n-validation', () => {
|
|||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
const cleaned = cleanWorkflowForUpdate(workflow);
|
const cleaned = cleanWorkflowForUpdate(workflow);
|
||||||
expect(cleaned.settings).toEqual({});
|
expect(cleaned).not.toHaveProperty('settings');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should omit settings when only non-whitelisted properties exist (Issue #431)', () => {
|
||||||
|
const workflow = {
|
||||||
|
name: 'Test Workflow',
|
||||||
|
nodes: [],
|
||||||
|
connections: {},
|
||||||
|
settings: {
|
||||||
|
callerPolicy: 'workflowsFromSameOwner' as const, // Filtered out
|
||||||
|
timeSavedPerExecution: 5, // Filtered out (UI-only)
|
||||||
|
someOtherProperty: 'value', // Filtered out
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const cleaned = cleanWorkflowForUpdate(workflow);
|
||||||
|
// All properties were filtered out, so settings should not exist
|
||||||
|
// to avoid "must NOT have additional properties" API error
|
||||||
|
expect(cleaned).not.toHaveProperty('settings');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should preserve whitelisted settings when mixed with non-whitelisted (Issue #431)', () => {
|
||||||
|
const workflow = {
|
||||||
|
name: 'Test Workflow',
|
||||||
|
nodes: [],
|
||||||
|
connections: {},
|
||||||
|
settings: {
|
||||||
|
executionOrder: 'v1' as const, // Whitelisted
|
||||||
|
callerPolicy: 'workflowsFromSameOwner' as const, // Filtered out
|
||||||
|
timezone: 'America/New_York', // Whitelisted
|
||||||
|
someOtherProperty: 'value', // Filtered out
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const cleaned = cleanWorkflowForUpdate(workflow);
|
||||||
|
// Should keep only whitelisted properties
|
||||||
|
expect(cleaned.settings).toEqual({
|
||||||
|
executionOrder: 'v1',
|
||||||
|
timezone: 'America/New_York'
|
||||||
|
});
|
||||||
|
expect(cleaned.settings).not.toHaveProperty('callerPolicy');
|
||||||
|
expect(cleaned.settings).not.toHaveProperty('someOtherProperty');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1346,7 +1387,7 @@ describe('n8n-validation', () => {
|
|||||||
expect(forUpdate).not.toHaveProperty('active');
|
expect(forUpdate).not.toHaveProperty('active');
|
||||||
expect(forUpdate).not.toHaveProperty('tags');
|
expect(forUpdate).not.toHaveProperty('tags');
|
||||||
expect(forUpdate).not.toHaveProperty('meta');
|
expect(forUpdate).not.toHaveProperty('meta');
|
||||||
expect(forUpdate.settings).toEqual({}); // Settings replaced with empty object for API compatibility
|
expect(forUpdate).not.toHaveProperty('settings'); // Settings omitted when not present (Issue #431)
|
||||||
expect(validateWorkflowStructure(forUpdate)).toEqual([]);
|
expect(validateWorkflowStructure(forUpdate)).toEqual([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user