n8n-io/n8n · error · ConnectionError

INVALID_CONNECTION

INVALID_CONNECTION

Error message

Invalid connection

What it means

Thrown by the connect_nodes tool after validateConnection() (utils/connection.utils) returns { valid: false }. The tool already inferred a connection type and applied its swap heuristic, but the final source/target/type topology is still not permitted by the node types' declared inputs and outputs. validation.error carries the specific reason when available, otherwise the generic 'Invalid connection' fallback is used.

Source

Thrown at packages/@n8n/ai-workflow-builder.ee/src/tools/connect-nodes.tool.ts:214

					`Connecting ${matchedSourceNode.name} to ${matchedTargetNode.name}...`,
				);

				// Validate connection and check if nodes need to be swapped
				const validation = validateConnection(
					matchedSourceNode,
					matchedTargetNode,
					connectionType,
					nodeTypes,
				);

				if (!validation.valid) {
					const connectionError = new ConnectionError(validation.error ?? 'Invalid connection', {
						fromNodeId: matchedSourceNode.id,
						toNodeId: matchedTargetNode.id,
					});
					const error = {
						message: connectionError.message,
						code: 'INVALID_CONNECTION',
						details: {
							sourceNode: matchedSourceNode.name,
							targetNode: matchedTargetNode.name,
							connectionType,
						},
					};
					reporter.error(error);
					return createErrorResponse(config, error);
				}

				// Use potentially swapped nodes
				const actualSourceNode = validation.swappedSource ?? matchedSourceNode;
				const actualTargetNode = validation.swappedTarget ?? matchedTargetNode;
				// Track if nodes were swapped either during inference or validation
				const swapped = inferredSwap || !!validation.shouldSwap;

				// Create only the new connection (not the full connections object)
				// This is important for parallel execution - each tool only returns its own connection

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Inspect details.connectionType and details.sourceNode/targetNode, then check each node type's inputs/outputs to find a genuinely compatible pair.
  2. Try the inverse direction explicitly (swap sourceNodeId and targetNodeId) in case inference picked the wrong orientation.
  3. Insert a bridging node (No-Op, Set, or Merge) whose inputs/outputs match both ends.

Example fix

// before: two triggers cannot connect
await connect_nodes.invoke({ sourceNodeId: triggerA, targetNodeId: triggerB });
// after: route through a Set node that accepts main input and emits main output
await connect_nodes.invoke({ sourceNodeId: triggerA, targetNodeId: setNode });
await connect_nodes.invoke({ sourceNodeId: setNode, targetNodeId: triggerB });
Defensive patterns

Strategy: validation

Validate before calling

// Before calling connect_nodes, confirm the two node types share a compatible
// connection type by inspecting their INodeTypeDescription outputs/inputs.
function findCompatibleConnectionType(src: INodeTypeDescription, tgt: INodeTypeDescription): string | null {
  const outTypes = new Set((src.outputs.main?.[0] ?? []).flatMap((o) => o));
  for (const input of tgt.inputs.main ?? []) {
    if (input.some((t) => outTypes.has(t))) return 'main';
  }
  // repeat for ai_* keys present on both sides
  return null;
}

const type = findCompatibleConnectionType(srcType, tgtType);
if (!type) { /* surface guidance to caller instead of invoking */ }

Type guard

function isToolError(v: unknown): v is { message: string; code: string; details?: unknown } {
  return typeof v === 'object' && v !== null && 'code' in v && 'message' in v;
}

Try / catch

// connect_nodes returns a structured error response rather than throwing, so
// inspect the returned object's code field rather than try/catch.
const res = await connect_nodes.invoke(input);
if (isToolError(res) && res.code === 'INVALID_CONNECTION') {
  // read res.details.connectionType / sourceNode / targetNode, pick a different pair
}

Prevention

When it happens

Trigger: Calling connect_nodes with two node IDs whose node types share no compatible output/input pair after type inference and swap; e.g. two trigger nodes, or two main nodes where a sub-node/main-node relationship is required.

Common situations: Trying to chain two trigger nodes (both only produce, neither consumes main); connecting a node whose only output type is ai_memory to a target with no ai_memory input; connecting nodes whose versions changed their IO declarations.

Related errors


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