{"record":{"id":"c89729004b1cda70","repo":"n8n-io/n8n","slug":"invalid-request","errorCode":"invalid_request","errorMessage":"invalid_request","messagePattern":"invalid_request","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"packages/@n8n/engine/src/server/routes/workflow-executions.ts","lineNumber":59,"sourceCode":"const WorkflowGraphSchema = z.object({\n\tnodes: z.array(GraphNodeSchema),\n\tedges: z.array(GraphEdgeSchema),\n});\n\nconst StartExecutionBody = z.object({\n\tworkflowId: z.string().min(1),\n\tgraph: WorkflowGraphSchema,\n\ttriggerPayload: jsonObjectSchema.nullable().optional(),\n\tmode: z.enum(['production', 'manual']).optional(),\n});\n\nexport function createWorkflowExecutionsRouter(startExecution: StartExecutionService): RouterType {\n\tconst router = Router();\n\n\trouter.post('/', async (req, res) => {\n\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}","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/engine/src/server/routes/workflow-executions.ts#L41-L77","documentation":"Returned as HTTP 400 {error:'invalid_request', details:...} when Zod's StartExecutionBody.safeParse(req.body) fails on POST /workflow-executions. The body schema requires workflowId (non-empty string), graph (nodes + edges matching GraphNodeSchema/GraphEdgeSchema), optional triggerPayload (JSON object or null), and optional mode ('production'|'manual'). details carries parsed.error.flatten() with field-level validation errors. This is a transport-layer rejection before any execution logic runs.","triggerScenarios":"POST /workflow-executions with: missing or empty workflowId; graph missing nodes or edges arrays; a node whose type is not one of 'trigger'|'v1-node'|'wait'|'subworkflow'|'batch'; an edge with a negative or non-integer outputIndex/inputIndex; mode set to an invalid enum value; triggerPayload that is a primitive instead of an object.","commonSituations":"Client sends a partially-built graph during development. A field rename (workflowId vs workflow_id) after an API version bump. Sending triggerPayload as a raw string instead of a JSON object. Copy-pasting a v1 workflow JSON whose shape doesn't match the new engine schema.","solutions":["Inspect response.body.details.fieldErrors — Zod reports the exact failing field and rule.","Validate the payload against StartExecutionBody on the client before POSTing (mirror the Zod schema).","Ensure workflowId is a non-empty string and graph has both nodes and edges arrays.","Confirm every node.type is in ['trigger','v1-node','wait','subworkflow','batch'] and every edge index is a non-negative integer.","If triggerPayload is present, make it a JSON object (z.record) or null, never a primitive."],"exampleFix":"// before — missing workflowId, bad node type\nconst body = { graph: { nodes: [{ id: 'n1', type: 'httpRequest' }], edges: [] } };\n// after — valid shape\nconst body = {\n  workflowId: 'wf-123',\n  graph: {\n    nodes: [{ id: 'n1', name: 'Start', type: 'trigger' }],\n    edges: []\n  }\n};","handlingStrategy":"validation","validationCode":"import { z } from 'zod';\nconst StepType = z.enum(['trigger','v1-node','wait','subworkflow','batch']);\nconst Edge = z.object({\n  from: z.string(), to: z.string(),\n  outputIndex: z.number().int().nonnegative().default(0),\n  inputIndex: z.number().int().nonnegative().default(0),\n  isBackEdge: z.boolean().optional(),\n});\nconst Graph = z.object({\n  nodes: z.array(z.object({ id: z.string(), name: z.string(), type: StepType, config: z.unknown().optional() })),\n  edges: z.array(Edge),\n});\nconst Body = z.object({\n  workflowId: z.string().min(1),\n  graph: Graph,\n  triggerPayload: z.record(z.unknown()).nullable().optional(),\n  mode: z.enum(['production','manual']).optional(),\n});\n// run before POST\nconst parsed = Body.safeParse(payload);\nif (!parsed.success) console.error(parsed.error.flatten());","typeGuard":"function isValidStartBody(body) {\n  return typeof body?.workflowId === 'string' && body.workflowId.length > 0\n    && Array.isArray(body?.graph?.nodes) && Array.isArray(body?.graph?.edges)\n    && body.graph.nodes.every(n => ['trigger','v1-node','wait','subworkflow','batch'].includes(n.type));\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_request') {\n    // body.details.fieldErrors pinpoints each failing field; fix and retry\n  }\n}","preventionTips":["Mirror the server's Zod schema on the client and validate before POSTing.","Always send workflowId as a non-empty string and graph with nodes+edges arrays.","Keep node.type within the StepType enum; keep edge indices as non-negative integers."],"tags":["engine","http","validation","zod","request-schema"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}