{"record":{"id":"b9b8b7c2b6c58e65","repo":"n8n-io/n8n","slug":"invalid-graph-b9b8b7","errorCode":"invalid_graph","errorMessage":"invalid_graph","messagePattern":"invalid_graph","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"packages/@n8n/engine/src/server/routes/workflow-executions.ts","lineNumber":75,"sourceCode":"\t\tconst parsed = StartExecutionBody.safeParse(req.body);\n\t\tif (!parsed.success) {\n\t\t\tres.status(400).json({\n\t\t\t\terror: 'invalid_request',\n\t\t\t\tdetails: parsed.error.flatten(),\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await startExecution.start(parsed.data);\n\t\t\tres.status(201).json(result);\n\t\t} catch (error) {\n\t\t\tif (error instanceof AdmittanceRejectedError) {\n\t\t\t\tres.status(429).json({ error: 'admittance_rejected', reason: error.reason });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (error instanceof GraphValidationError) {\n\t\t\t\tres.status(400).json({ error: 'invalid_graph', reason: error.message });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (error instanceof UnimplementedError) {\n\t\t\t\tres.status(501).json({ error: 'unimplemented', reason: error.message });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t});\n\n\treturn router;\n}\n","sourceCodeStart":57,"sourceCodeEnd":88,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/engine/src/server/routes/workflow-executions.ts#L57-L88","documentation":"Returned as HTTP 400 {error:'invalid_graph', reason:...} when startExecution.start() throws GraphValidationError. validateExecutableGraph() throws this class for graphs that can NEVER run: zero trigger nodes, more than one trigger node, a slot index that is not a non-negative integer, or a slot index above MAX_SLOT_INDEX (100). Unlike UnimplementedError (501), these are hard structural defects, not missing features.","triggerScenarios":"POST /workflow-executions with a graph that has no node of type 'trigger'; a graph with two 'trigger' nodes; an edge whose outputIndex or inputIndex is a float, negative, or greater than 100. The reason field echoes the exact GraphValidationError message.","commonSituations":"Building a graph programmatically and forgetting the trigger node. Duplicating a trigger node during composition. Sending connection indices derived from a zero-based vs one-based mismatch. Sending a placeholder index like 999 that exceeds MAX_SLOT_INDEX.","solutions":["Ensure exactly one node has type 'trigger'.","Ensure every edge.outputIndex and edge.inputIndex is an integer in [0, 100].","Read response.body.reason — it names the failing rule and the offending edge/node.","Run validateExecutableGraph() (or an equivalent local check) before POSTing during development."],"exampleFix":"// before — no trigger node, invalid slot index\ngraph = {\n  nodes: [{ id: 'n1', name: 'Do', type: 'v1-node' }],\n  edges: [{ from: 'n1', to: 'n1', outputIndex: -1, inputIndex: 0 }]\n};\n// after — one trigger node, non-negative integer indices\ngraph = {\n  nodes: [\n    { id: 't', name: 'Start', type: 'trigger' },\n    { id: 'n1', name: 'Do', type: 'v1-node' }\n  ],\n  edges: [{ from: 't', to: 'n1', outputIndex: 0, inputIndex: 0 }]\n};","handlingStrategy":"validation","validationCode":"const MAX_SLOT_INDEX = 100;\nfunction assertValidGraphStructure(graph) {\n  const triggers = graph.nodes.filter(n => n.type === 'trigger');\n  if (triggers.length !== 1) throw new Error(`Expected exactly 1 trigger, got ${triggers.length}`);\n  for (const e of graph.edges) {\n    for (const idx of [e.outputIndex ?? 0, e.inputIndex ?? 0]) {\n      if (!Number.isInteger(idx) || idx < 0 || idx > MAX_SLOT_INDEX) {\n        throw new Error(`Bad slot index ${idx} on edge ${e.from}->${e.to}`);\n      }\n    }\n  }\n}","typeGuard":"function isStructurallyValidGraph(graph) {\n  const triggers = graph.nodes.filter(n => n.type === 'trigger');\n  if (triggers.length !== 1) return false;\n  return graph.edges.every(e =>\n    [e.outputIndex ?? 0, e.inputIndex ?? 0].every(i => Number.isInteger(i) && i >= 0 && i <= 100)\n  );\n}","tryCatchPattern":"const res = await fetch('/workflow-executions', { method: 'POST', body: JSON.stringify(payload) });\nif (res.status === 400) {\n  const body = await res.json();\n  if (body.error === 'invalid_graph') {\n    // body.reason names the exact structural rule that failed\n  }\n}","preventionTips":["Always include exactly one node of type 'trigger'.","Keep every edge index an integer in [0, 100].","Run assertValidGraphStructure(graph) locally during development.","Read body.reason on a 400 invalid_graph to pinpoint the offending node/edge."],"tags":["engine","http","graph-validation","validation"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}