Problem
When n8n rejects a workflow write with
request/body/settings must NOT have additional properties
the error never names the offending property. n8n's AJV validator reports only that some
extra key was present. n8n-mcp passes that message straight through, so neither the user nor the
maintainer can tell which setting broke the write.
Diagnosing an instance of this class today requires diffing n8n's IWorkflowSettings (the entity,
what GET returns) against updateWorkflow.generated.yml (the schema, what PUT accepts) at the
right n8n version. That is how #1043 / #1044 (engineType) was eventually found — by reading n8n
source, not from any signal n8n-mcp produced.
Why the information is unavailable today
handleN8nApiError (src/utils/n8n-errors.ts) maps a 400 to
new N8nValidationError(message, data), where data is n8n's response body. The request
payload — the settings object we actually sent — is never captured, so the keys are structurally
unavailable by the time anyone reads the error.
scripts/check-settings-drift.ts cannot cover the gap either: it diffs our table against n8n's
OpenAPI schema, and an entity-only property is absent from both, so they agree and it passes
green. The breaking axis is entity-vs-schema, which that script does not look at.
Net effect: this class of failure is silent until a user reports a broken write.
Impact
On a hosted multi-tenant deployment, container logs for a single 24h window showed 14
body/settings must NOT have additional properties rejections across ~10 distinct accounts —
plus 6 of a body-level variant (below). These do not show up in success/error metrics,
because the MCP call returns a success: false payload inside an HTTP 200.
Proposal
There is already precedent for exactly this enrichment: folderPlacementHint() pattern-matches
parentFolderId + additional propert and appends an actionable hint. Generalise that to settings.
On a 400 whose message matches /body\/settings.*additional ?propert/i, append the keys that were
sent, and flag those unknown to WORKFLOW_SETTINGS_PROPERTIES:
n8n rejected workflow settings. Sent keys: executionOrder, binaryMode, timeSavedMode,
callerPolicy, availableInMCP, engineType. Not in n8n-mcp's known-settings table: engineType.
This is entity-vs-schema drift — n8n stores the key but its Public API rejects it. Please report
it with this key name.
Key names only, never values — settings can hold errorWorkflow ids and telemetry tags.
That turns each future recurrence from a source-diffing exercise into a single log line, and makes
user reports self-identifying. Given this is the third instance of the pattern (#248
timeSavedPerExecution, #466, #1043 engineType), a fourth seems likely; each new n8n minor can
add an entity-only setting.
Implementation is small: handleN8nApiError needs the request payload's settings keys in scope
(or the hint applied one level up, where the payload is still available).
Related: an unfixed body-level variant
The same 24h window showed 6 occurrences of
request/body must NOT have additional properties
— no /settings segment, so this is a top-level workflow property, not a setting.
stripDerivedSettings does nothing for it, and #1044 does not address it. The proposed diagnostic
should cover this case too (report the top-level keys sent), since right now there is no way to
tell which property is at fault.
Environment
- n8n-mcp 2.76.1
- Reproduced against n8n 2.36.7 and 2.36.9
Note: on both 2.36.7 and 2.36.9 I could not get n8n to emit settings.engineType at all —
neither a workflow created via the Public API nor one created and edited in the UI carried it
(settings came back as executionOrder, binaryMode, timeSavedMode, callerPolicy,
availableInMCP, timeSavedPerExecution). So engineType appears to be conditional on
configuration rather than emitted by every 2.36.x instance — worth confirming, as it affects who
is actually exposed to #1043.
Problem
When n8n rejects a workflow write with
the error never names the offending property. n8n's AJV validator reports only that some
extra key was present. n8n-mcp passes that message straight through, so neither the user nor the
maintainer can tell which setting broke the write.
Diagnosing an instance of this class today requires diffing n8n's
IWorkflowSettings(the entity,what
GETreturns) againstupdateWorkflow.generated.yml(the schema, whatPUTaccepts) at theright n8n version. That is how #1043 / #1044 (
engineType) was eventually found — by reading n8nsource, not from any signal n8n-mcp produced.
Why the information is unavailable today
handleN8nApiError(src/utils/n8n-errors.ts) maps a 400 tonew N8nValidationError(message, data), wheredatais n8n's response body. The requestpayload — the settings object we actually sent — is never captured, so the keys are structurally
unavailable by the time anyone reads the error.
scripts/check-settings-drift.tscannot cover the gap either: it diffs our table against n8n'sOpenAPI schema, and an entity-only property is absent from both, so they agree and it passes
green. The breaking axis is entity-vs-schema, which that script does not look at.
Net effect: this class of failure is silent until a user reports a broken write.
Impact
On a hosted multi-tenant deployment, container logs for a single 24h window showed 14
body/settings must NOT have additional propertiesrejections across ~10 distinct accounts —plus 6 of a body-level variant (below). These do not show up in success/error metrics,
because the MCP call returns a
success: falsepayload inside an HTTP 200.Proposal
There is already precedent for exactly this enrichment:
folderPlacementHint()pattern-matchesparentFolderId+additional propertand appends an actionable hint. Generalise that to settings.On a 400 whose message matches
/body\/settings.*additional ?propert/i, append the keys that weresent, and flag those unknown to
WORKFLOW_SETTINGS_PROPERTIES:Key names only, never values — settings can hold
errorWorkflowids and telemetry tags.That turns each future recurrence from a source-diffing exercise into a single log line, and makes
user reports self-identifying. Given this is the third instance of the pattern (#248
timeSavedPerExecution, #466, #1043engineType), a fourth seems likely; each new n8n minor canadd an entity-only setting.
Implementation is small:
handleN8nApiErrorneeds the request payload's settings keys in scope(or the hint applied one level up, where the payload is still available).
Related: an unfixed body-level variant
The same 24h window showed 6 occurrences of
— no
/settingssegment, so this is a top-level workflow property, not a setting.stripDerivedSettingsdoes nothing for it, and #1044 does not address it. The proposed diagnosticshould cover this case too (report the top-level keys sent), since right now there is no way to
tell which property is at fault.
Environment
Note: on both 2.36.7 and 2.36.9 I could not get n8n to emit
settings.engineTypeat all —neither a workflow created via the Public API nor one created and edited in the UI carried it
(settings came back as
executionOrder,binaryMode,timeSavedMode,callerPolicy,availableInMCP,timeSavedPerExecution). SoengineTypeappears to be conditional onconfiguration rather than emitted by every 2.36.x instance — worth confirming, as it affects who
is actually exposed to #1043.