You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On n8nac 2.6.0, n8nac push runs runPrePushValidation and aborts on any finding. On our estate that blocks pushes of workflows the instance itself accepts, and for workflows over 50 nodes the instance check never runs at all.
Measured over all 231 of our tracked workflows (every one live and working): 93 are blocked by the bundled check alone. Instance is n8n 2.37.10, self-hosted.
Four separate problems. Each repro below is a minimal synthetic workflow, checked by calling PreflightNodeValidator from n8nac@2.6.0 directly: once with no endpoint (bundled), once with the instance endpoint (level ≥ 2). That is the class runPrePushValidation uses.
1. The bundled check blocks nodes the instance validator accepts
The bundled path is described as running "the server-equivalent gating rules". For these nodes it reports errors, and the instance's own validate_node_config returns valid: true for the same payload:
Omitted default values are not applied before gating. n8n does not store a parameter that equals its default, so a Slack "post message" node has no resource/operation in its JSON. The gate then treats text/channelId as not allowed. About 100 of our nodes hit this, across Slack v1–v2.4, Telegram v1.1/v1.2, Google Drive v3, Merge v2.1, Gmail v2.1 and OpenAI v1.4. We also checked Slack v1, Filter v1, Google Drive v3 and Telegram v1.1 nodes from our workflows against the instance: all valid.
Bundled: typeVersion 2.2 does not exist for node "@n8n/n8n-nodes-langchain.toolWorkflow". Valid versions: [1, 1.1, 1.2, 1.3]. Use 1.3 (latest).
Instance: valid, and get_node_types on the instance returns the v2.2 definition. n8n-nodes-technical.json in skills 2.4.0 lists only [1, 1.1, 1.2, 1.3]. 38 of our nodes are on 2.x, and "Use 1.3" is not a safe fix: it changes the parameter shape.
3. Workflows with more than 50 nodes are never validated by the instance
validate_node_config on the instance rejects more than 50 nodes per call:
Input validation error: Invalid arguments for tool validate_node_config: nodes: Array must contain at most 50 element(s)
callServerValidate() (dist/core/services/preflight-node-validator.js) sends every node in one call. It then runs JSON.parse on that error text, which throws Unexpected token 'I', "Input vali"... is not valid JSON, and validateFile() falls back to the bundled check. A workflow of a trigger + 51 noOp nodes shows it: source: 'local', serverUnavailableReason: Unexpected token 'I' .... So on large workflows the authoritative check silently never runs, and the printed reason doesn't point at the cause. Batching in chunks of 50, and checking isError before parsing, would fix both.
4. Existing native-MCP users get the strictest level without opting in
effectiveNativeMcpLevel() (dist/services/config-service.js) ends with return nativeMcp.enabled || nativeMcp.url ? 3 : 0;. An environment configured before 2.6.0 (nativeMcp: { enabled: true, url, mode: "assist" }, no level) therefore validates live on every push after upgrading. I couldn't find a note on that behaviour change in the release notes. The only way out is N8NAC_PUSH_SKIP_VALIDATION=1, which also switches the check off for new workflows, where it is genuinely useful.
Related: the instance validator itself isn't a safe hard gate either. It rejects Baserow nodes with numeric databaseId/tableId (Type mismatches: "parameters.databaseId" (expected string, got number)) and accepts "1". Numbers are what the node's getDatabaseIds/getTableIds load-options return, so that is what the editor stores and what runs. That part is n8n core's validator, not n8nac, but it means blocking on the instance's answer alone would also stop working workflows (88 of our 231 have a Baserow node).
Suggestion
Block only on findings the instance confirms. Treat bundled-only findings as warnings until the bundled rules apply @version scoping and defaults.
Chunk validate_node_config calls to ≤ 50 nodes and handle isError results.
Make the blocking gate opt-in for environments configured before 2.6.0, or at least call it out in the release notes.
Happy to test a pre-release against our workflows. The measurement script runs read-only over a whole workspace.
Summary
On n8nac 2.6.0,
n8nac pushrunsrunPrePushValidationand aborts on any finding. On our estate that blocks pushes of workflows the instance itself accepts, and for workflows over 50 nodes the instance check never runs at all.Measured over all 231 of our tracked workflows (every one live and working): 93 are blocked by the bundled check alone. Instance is n8n 2.37.10, self-hosted.
Four separate problems. Each repro below is a minimal synthetic workflow, checked by calling
PreflightNodeValidatorfromn8nac@2.6.0directly: once with no endpoint (bundled), once with the instance endpoint (level ≥ 2). That is the classrunPrePushValidationuses.1. The bundled check blocks nodes the instance validator accepts
The bundled path is described as running "the server-equivalent gating rules". For these nodes it reports errors, and the instance's own
validate_node_configreturnsvalid: truefor the same payload:validate_node_confign8n-nodes-base.ifv1 withconditions.string[]Missing required parameter: "nodes[IF].parameters.conditions.options"n8n-nodes-base.slackv2.2,resource/operationomitted (defaults)Field "parameters.select": This field is only allowed when one of: (resource="message", operation="post", ...)(same forchannelId,text){ "name": "IF", "type": "n8n-nodes-base.if", "typeVersion": 1, "parameters": { "conditions": { "string": [ { "value1": "={{ $json.status }}", "value2": "paid" } ] } } } { "name": "Slack", "type": "n8n-nodes-base.slack", "typeVersion": 2.2, "parameters": { "select": "channel", "channelId": { "__rl": true, "value": "C0000000000", "mode": "id" }, "text": "hello", "otherOptions": {} } }Two causes, as far as I can tell:
typeVersionis not respected for IF / Filter v1.conditions.optionsonly exists from v2. 119 of our nodes are IF v1 or Filter v1. This is the legacy-version class left out of skills validate: multiOptions values, @version-scoped properties and defaulted required params reported as errors #609, but it now blocks a push instead of printing a hint.resource/operationin its JSON. The gate then treatstext/channelIdas not allowed. About 100 of our nodes hit this, across Slack v1–v2.4, Telegram v1.1/v1.2, Google Drive v3, Merge v2.1, Gmail v2.1 and OpenAI v1.4. We also checked Slack v1, Filter v1, Google Drive v3 and Telegram v1.1 nodes from our workflows against the instance: all valid.2. Bundled node versions are behind the instance
{ "name": "Tool", "type": "@n8n/n8n-nodes-langchain.toolWorkflow", "typeVersion": 2.2, "parameters": { "description": "demo", "workflowId": { "__rl": true, "value": "abc", "mode": "id" }, "workflowInputs": { "mappingMode": "defineBelow", "value": {}, "matchingColumns": [], "schema": [], "attemptToConvertTypes": false, "convertFieldsToString": false } } }Bundled:
typeVersion 2.2 does not exist for node "@n8n/n8n-nodes-langchain.toolWorkflow". Valid versions: [1, 1.1, 1.2, 1.3]. Use 1.3 (latest).Instance: valid, and
get_node_typeson the instance returns the v2.2 definition.n8n-nodes-technical.jsonin skills 2.4.0 lists only[1, 1.1, 1.2, 1.3]. 38 of our nodes are on 2.x, and "Use 1.3" is not a safe fix: it changes the parameter shape.3. Workflows with more than 50 nodes are never validated by the instance
validate_node_configon the instance rejects more than 50 nodes per call:callServerValidate()(dist/core/services/preflight-node-validator.js) sends every node in one call. It then runsJSON.parseon that error text, which throwsUnexpected token 'I', "Input vali"... is not valid JSON, andvalidateFile()falls back to the bundled check. A workflow of a trigger + 51noOpnodes shows it:source: 'local',serverUnavailableReason: Unexpected token 'I' .... So on large workflows the authoritative check silently never runs, and the printed reason doesn't point at the cause. Batching in chunks of 50, and checkingisErrorbefore parsing, would fix both.4. Existing native-MCP users get the strictest level without opting in
effectiveNativeMcpLevel()(dist/services/config-service.js) ends withreturn nativeMcp.enabled || nativeMcp.url ? 3 : 0;. An environment configured before 2.6.0 (nativeMcp: { enabled: true, url, mode: "assist" }, nolevel) therefore validates live on every push after upgrading. I couldn't find a note on that behaviour change in the release notes. The only way out isN8NAC_PUSH_SKIP_VALIDATION=1, which also switches the check off for new workflows, where it is genuinely useful.Related: the instance validator itself isn't a safe hard gate either. It rejects Baserow nodes with numeric
databaseId/tableId(Type mismatches: "parameters.databaseId" (expected string, got number)) and accepts"1". Numbers are what the node'sgetDatabaseIds/getTableIdsload-options return, so that is what the editor stores and what runs. That part is n8n core's validator, not n8nac, but it means blocking on the instance's answer alone would also stop working workflows (88 of our 231 have a Baserow node).Suggestion
@versionscoping and defaults.validate_node_configcalls to ≤ 50 nodes and handleisErrorresults.Happy to test a pre-release against our workflows. The measurement script runs read-only over a whole workspace.
Environment