n8n-io/n8n · error
invalid_request
invalid_request
Error message
invalid_request
What it means
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.
Source
Thrown at packages/@n8n/engine/src/server/routes/workflow-executions.ts:59
const WorkflowGraphSchema = z.object({
nodes: z.array(GraphNodeSchema),
edges: z.array(GraphEdgeSchema),
});
const StartExecutionBody = z.object({
workflowId: z.string().min(1),
graph: WorkflowGraphSchema,
triggerPayload: jsonObjectSchema.nullable().optional(),
mode: z.enum(['production', 'manual']).optional(),
});
export function createWorkflowExecutionsRouter(startExecution: StartExecutionService): RouterType {
const router = Router();
router.post('/', async (req, res) => {
const parsed = StartExecutionBody.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({
error: 'invalid_request',
details: parsed.error.flatten(),
});
return;
}
try {
const result = await startExecution.start(parsed.data);
res.status(201).json(result);
} catch (error) {
if (error instanceof AdmittanceRejectedError) {
res.status(429).json({ error: 'admittance_rejected', reason: error.reason });
return;
}
if (error instanceof GraphValidationError) {
res.status(400).json({ error: 'invalid_graph', reason: error.message });
return;
}View on GitHub (pinned to 5ac6606e81)
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.
Example fix
// before — missing workflowId, bad node type
const body = { graph: { nodes: [{ id: 'n1', type: 'httpRequest' }], edges: [] } };
// after — valid shape
const body = {
workflowId: 'wf-123',
graph: {
nodes: [{ id: 'n1', name: 'Start', type: 'trigger' }],
edges: []
}
}; Defensive patterns
Strategy: validation
Validate before calling
import { z } from 'zod';
const StepType = z.enum(['trigger','v1-node','wait','subworkflow','batch']);
const Edge = z.object({
from: z.string(), to: z.string(),
outputIndex: z.number().int().nonnegative().default(0),
inputIndex: z.number().int().nonnegative().default(0),
isBackEdge: z.boolean().optional(),
});
const Graph = z.object({
nodes: z.array(z.object({ id: z.string(), name: z.string(), type: StepType, config: z.unknown().optional() })),
edges: z.array(Edge),
});
const Body = z.object({
workflowId: z.string().min(1),
graph: Graph,
triggerPayload: z.record(z.unknown()).nullable().optional(),
mode: z.enum(['production','manual']).optional(),
});
// run before POST
const parsed = Body.safeParse(payload);
if (!parsed.success) console.error(parsed.error.flatten()); Type guard
function isValidStartBody(body) {
return typeof body?.workflowId === 'string' && body.workflowId.length > 0
&& Array.isArray(body?.graph?.nodes) && Array.isArray(body?.graph?.edges)
&& body.graph.nodes.every(n => ['trigger','v1-node','wait','subworkflow','batch'].includes(n.type));
} Try / catch
const res = await fetch('/workflow-executions', { method: 'POST', body: JSON.stringify(payload) });
if (res.status === 400) {
const body = await res.json();
if (body.error === 'invalid_request') {
// body.details.fieldErrors pinpoints each failing field; fix and retry
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- invalid_graph
- Tool "${this.name}" requires an input schema
- VALIDATION_ERROR
- VALIDATION_ERROR
- VALIDATION_ERROR
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/c89729004b1cda70.
Report an issue: GitHub.