{"record":{"id":"2f9f79d71956a0bd","repo":"n8n-io/n8n","slug":"invalid-connection","errorCode":"INVALID_CONNECTION","errorMessage":"Invalid connection","messagePattern":"Invalid connection","errorType":"error_code","errorClass":"ConnectionError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/ai-workflow-builder.ee/src/tools/connect-nodes.tool.ts","lineNumber":214,"sourceCode":"\t\t\t\t\t`Connecting ${matchedSourceNode.name} to ${matchedTargetNode.name}...`,\n\t\t\t\t);\n\n\t\t\t\t// Validate connection and check if nodes need to be swapped\n\t\t\t\tconst validation = validateConnection(\n\t\t\t\t\tmatchedSourceNode,\n\t\t\t\t\tmatchedTargetNode,\n\t\t\t\t\tconnectionType,\n\t\t\t\t\tnodeTypes,\n\t\t\t\t);\n\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\tconst connectionError = new ConnectionError(validation.error ?? 'Invalid connection', {\n\t\t\t\t\t\tfromNodeId: matchedSourceNode.id,\n\t\t\t\t\t\ttoNodeId: matchedTargetNode.id,\n\t\t\t\t\t});\n\t\t\t\t\tconst error = {\n\t\t\t\t\t\tmessage: connectionError.message,\n\t\t\t\t\t\tcode: 'INVALID_CONNECTION',\n\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\tsourceNode: matchedSourceNode.name,\n\t\t\t\t\t\t\ttargetNode: matchedTargetNode.name,\n\t\t\t\t\t\t\tconnectionType,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t\treporter.error(error);\n\t\t\t\t\treturn createErrorResponse(config, error);\n\t\t\t\t}\n\n\t\t\t\t// Use potentially swapped nodes\n\t\t\t\tconst actualSourceNode = validation.swappedSource ?? matchedSourceNode;\n\t\t\t\tconst actualTargetNode = validation.swappedTarget ?? matchedTargetNode;\n\t\t\t\t// Track if nodes were swapped either during inference or validation\n\t\t\t\tconst swapped = inferredSwap || !!validation.shouldSwap;\n\n\t\t\t\t// Create only the new connection (not the full connections object)\n\t\t\t\t// This is important for parallel execution - each tool only returns its own connection","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/ai-workflow-builder.ee/src/tools/connect-nodes.tool.ts#L196-L232","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect details.connectionType and details.sourceNode/targetNode, then check each node type's inputs/outputs to find a genuinely compatible pair.","Try the inverse direction explicitly (swap sourceNodeId and targetNodeId) in case inference picked the wrong orientation.","Insert a bridging node (No-Op, Set, or Merge) whose inputs/outputs match both ends."],"exampleFix":"// before: two triggers cannot connect\nawait connect_nodes.invoke({ sourceNodeId: triggerA, targetNodeId: triggerB });\n// after: route through a Set node that accepts main input and emits main output\nawait connect_nodes.invoke({ sourceNodeId: triggerA, targetNodeId: setNode });\nawait connect_nodes.invoke({ sourceNodeId: setNode, targetNodeId: triggerB });","handlingStrategy":"validation","validationCode":"// Before calling connect_nodes, confirm the two node types share a compatible\n// connection type by inspecting their INodeTypeDescription outputs/inputs.\nfunction findCompatibleConnectionType(src: INodeTypeDescription, tgt: INodeTypeDescription): string | null {\n  const outTypes = new Set((src.outputs.main?.[0] ?? []).flatMap((o) => o));\n  for (const input of tgt.inputs.main ?? []) {\n    if (input.some((t) => outTypes.has(t))) return 'main';\n  }\n  // repeat for ai_* keys present on both sides\n  return null;\n}\n\nconst type = findCompatibleConnectionType(srcType, tgtType);\nif (!type) { /* surface guidance to caller instead of invoking */ }","typeGuard":"function isToolError(v: unknown): v is { message: string; code: string; details?: unknown } {\n  return typeof v === 'object' && v !== null && 'code' in v && 'message' in v;\n}","tryCatchPattern":"// connect_nodes returns a structured error response rather than throwing, so\n// inspect the returned object's code field rather than try/catch.\nconst res = await connect_nodes.invoke(input);\nif (isToolError(res) && res.code === 'INVALID_CONNECTION') {\n  // read res.details.connectionType / sourceNode / targetNode, pick a different pair\n}","preventionTips":["Maintain a registry of node type IO signatures and pre-validate pairs before invoking connect_nodes.","Prefer connecting through well-known bridge nodes (No-Op, Set) when type compatibility is uncertain.","Log the inferred connectionType on success to build a compatibility map over time."],"tags":["workflow-builder","connections","validation","node-types"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}