n8n-io/n8n · error

unimplemented

unimplemented

Error message

unimplemented

What it means

Returned as HTTP 501 {error:'unimplemented', reason:...} when startExecution.start() throws UnimplementedError. validateExecutableGraph() throws this for graph shapes the engine does not support yet: back-edges/loops (CAT-2875), multi-slot outputs (CAT-2874, error 300), and same-slot convergence (CAT-3982, error 301). The graph passed schema and structural checks but uses a not-yet-built execution path.

Source

Thrown at packages/@n8n/engine/src/server/routes/workflow-executions.ts:79

				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;
			}
			if (error instanceof UnimplementedError) {
				res.status(501).json({ error: 'unimplemented', reason: error.message });
				return;
			}
			throw error;
		}
	});

	return router;
}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Read response.body.reason to identify which unimplemented path was hit (loops, multi-slot output, or convergence).
  2. For back-edges: unroll the loop or move the iterative logic into a single node.
  3. For multi-slot output: collapse to outputIndex 0 (see error 300).
  4. For convergence: route branches into distinct inputIndex values (see error 301).
  5. If the feature is required, track the corresponding CAT ticket and stay on the v1 engine until it lands.

Example fix

// before — loop via back-edge
edges: [{ from: 'process', to: 'check', inputIndex: 0 }, { from: 'check', to: 'process', inputIndex: 0, isBackEdge: true }]
// after — remove the back-edge; do iteration inside one node
edges: [{ from: 'process', to: 'check', inputIndex: 0 }]
Defensive patterns

Strategy: validation

Validate before calling

function assertNoUnsupportedFeatures(graph) {
  if (graph.edges.some(e => e.isBackEdge)) throw new Error('Loops not supported (CAT-2875)');
  if (graph.edges.some(e => e.outputIndex !== undefined && e.outputIndex !== 0)) throw new Error('Multi-slot output not supported (CAT-2874)');
  const seen = new Set();
  for (const e of graph.edges) {
    const slot = `${e.to}#${e.inputIndex ?? 0}`;
    if (seen.has(slot)) throw new Error('Convergence not supported (CAT-3982)');
    seen.add(slot);
  }
}

Type guard

function isFullySupportedGraph(graph) {
  if (graph.edges.some(e => e.isBackEdge)) return false;
  if (graph.edges.some(e => e.outputIndex !== undefined && e.outputIndex !== 0)) return false;
  const seen = new Set();
  for (const e of graph.edges) {
    const slot = `${e.to}#${e.inputIndex ?? 0}`;
    if (seen.has(slot)) return false;
    seen.add(slot);
  }
  return true;
}

Try / catch

const res = await fetch('/workflow-executions', { method: 'POST', body: JSON.stringify(payload) });
if (res.status === 501) {
  const body = await res.json(); // body.error === 'unimplemented', body.reason names the feature
  // restructure the graph to avoid the unsupported feature, or use the v1 engine
}

Prevention

When it happens

Trigger: POST /workflow-executions with a graph containing an edge where isBackEdge is true (a loop), an edge with outputIndex !== 0, or two edges into the same target input slot. Each maps to the same 501 envelope but a distinct reason string.

Common situations: Porting loop/iteration workflows before CAT-2875. Porting branching (IF/Switch) before CAT-2874. Porting Merge/fan-in before CAT-3982. The reason string tells you which feature gate was hit.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/ff3b99baae6078e5. Report an issue: GitHub.