Skip to content

Surface the offending key when n8n rejects workflow settings with "must NOT have additional properties" #1047

Description

@czlonkowski

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions