Summary
n8n_create_workflow returns an unhandled TypeError instead of a validation error when the
nodes array contains an entry that is not a well-formed node object and that entry is
referenced from connections.
The error surfaces to the client as:
Cannot read properties of undefined (reading 'toLowerCase')
A related shape returns node.type?.startsWith is not a function.
Both look like connection/type resolution running before (or independently of) the node-shape
validation that already exists and works correctly for the same inputs.
Reproduction
n8n-mcp 2.79.0, node v22.23.2, HTTP transport.
1. Crash — string entry in nodes[] that connections references:
{
"name": "repro",
"nodes": [
{"id": "1", "name": "Manual Trigger", "type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1, "position": [0, 0], "parameters": {}},
"strayString"
],
"connections": {
"Manual Trigger": {"main": [[{"node": "strayString", "type": "main", "index": 0}]]}
}
}
→ Cannot read properties of undefined (reading 'toLowerCase')
2. Crash — node whose type is not a string:
{
"name": "repro",
"nodes": [
{"id": "1", "name": "Manual Trigger", "type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1, "position": [0, 0], "parameters": {}},
{"id": "2", "name": "Num Type", "type": 123, "typeVersion": 1,
"position": [200, 0], "parameters": {}}
],
"connections": {}
}
→ node.type?.startsWith is not a function
What already works (for contrast)
These same malformed inputs are handled correctly when the bad entry is not wired up, which is
why this reads as a missing guard rather than a missing validator:
| Input |
Result |
String entry in nodes[], not referenced by connections |
Workflow validation failed ✅ |
Node object missing type, not referenced |
Workflow validation failed ✅ |
| Connection pointing at a node name that does not exist (all nodes valid) |
Workflow validation failed ✅ |
| Connection key naming a node that does not exist |
Workflow validation failed ✅ |
String entry in nodes[] that connections references |
TypeError ❌ |
Node with non-string type |
TypeError ❌ |
validate_workflow handles every one of the above cleanly (valid: false with 2 errors), so the
gap is specific to the create path.
Why it matters
Any client that builds workflow JSON programmatically can emit a malformed nodes entry — a
partially recovered JSON payload, a template that interpolated wrong, a serialisation bug. The
current behaviour turns that into an opaque runtime error that looks like a server fault, so
clients cannot distinguish "my payload was malformed" from "the server is broken", and it is not
actionable without server logs.
Suggested fix
Before connection/type resolution in the create path, reject entries in nodes that are not
objects with string name and type, and surface them through the same
Workflow validation failed response used elsewhere. Optionally guard the accessors
(node?.type?.toLowerCase?.()) as defence in depth.
Summary
n8n_create_workflowreturns an unhandledTypeErrorinstead of a validation error when thenodesarray contains an entry that is not a well-formed node object and that entry isreferenced from
connections.The error surfaces to the client as:
A related shape returns
node.type?.startsWith is not a function.Both look like connection/type resolution running before (or independently of) the node-shape
validation that already exists and works correctly for the same inputs.
Reproduction
n8n-mcp
2.79.0, nodev22.23.2, HTTP transport.1. Crash — string entry in
nodes[]thatconnectionsreferences:{ "name": "repro", "nodes": [ {"id": "1", "name": "Manual Trigger", "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [0, 0], "parameters": {}}, "strayString" ], "connections": { "Manual Trigger": {"main": [[{"node": "strayString", "type": "main", "index": 0}]]} } }→
Cannot read properties of undefined (reading 'toLowerCase')2. Crash — node whose
typeis not a string:{ "name": "repro", "nodes": [ {"id": "1", "name": "Manual Trigger", "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [0, 0], "parameters": {}}, {"id": "2", "name": "Num Type", "type": 123, "typeVersion": 1, "position": [200, 0], "parameters": {}} ], "connections": {} }→
node.type?.startsWith is not a functionWhat already works (for contrast)
These same malformed inputs are handled correctly when the bad entry is not wired up, which is
why this reads as a missing guard rather than a missing validator:
nodes[], not referenced byconnectionsWorkflow validation failed✅type, not referencedWorkflow validation failed✅Workflow validation failed✅Workflow validation failed✅nodes[]thatconnectionsreferencestypevalidate_workflowhandles every one of the above cleanly (valid: falsewith 2 errors), so thegap is specific to the create path.
Why it matters
Any client that builds workflow JSON programmatically can emit a malformed
nodesentry — apartially recovered JSON payload, a template that interpolated wrong, a serialisation bug. The
current behaviour turns that into an opaque runtime error that looks like a server fault, so
clients cannot distinguish "my payload was malformed" from "the server is broken", and it is not
actionable without server logs.
Suggested fix
Before connection/type resolution in the create path, reject entries in
nodesthat are notobjects with string
nameandtype, and surface them through the sameWorkflow validation failedresponse used elsewhere. Optionally guard the accessors(
node?.type?.toLowerCase?.()) as defence in depth.