{"record":{"id":"b342c076435bffc1","repo":"windmill-labs/windmill","slug":"group-g-id-references-virtual-node-groups-ca","errorCode":null,"errorMessage":"Group '${g.id}' references virtual node: groups cannot include Input, Result, or Trigger","messagePattern":"Group '(.+?)' references virtual node: groups cannot include Input, Result, or Trigger","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/graph/flowStructure.ts","lineNumber":91,"sourceCode":"\n\tconst indexMap = new Map<string, number>()\n\tfor (let i = 0; i < modules.length; i++) {\n\t\tindexMap.set(modules[i].id, i)\n\t}\n\n\t// Reject duplicate group IDs\n\tconst seenGroupIds = new Set<string>()\n\tfor (const g of groups) {\n\t\tif (seenGroupIds.has(g.id)) {\n\t\t\tthrow new Error(`Duplicate group id: '${g.id}'`)\n\t\t}\n\t\tseenGroupIds.add(g.id)\n\t}\n\n\t// Reject groups referencing virtual nodes\n\tfor (const g of groups) {\n\t\tif (VIRTUAL_NODE_IDS.has(g.start_id) || VIRTUAL_NODE_IDS.has(g.end_id)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Group '${g.id}' references virtual node: groups cannot include Input, Result, or Trigger`\n\t\t\t)\n\t\t}\n\t}\n\n\t// Partition: groups for this level vs rest\n\tconst levelGroups: GraphGroup[] = []\n\tconst otherGroups: GraphGroup[] = []\n\tfor (const g of groups) {\n\t\tif (indexMap.has(g.start_id) && indexMap.has(g.end_id)) {\n\t\t\tconst s = indexMap.get(g.start_id)!\n\t\t\tconst e = indexMap.get(g.end_id)!\n\t\t\tif (s > e) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Group '${g.id}' has inverted range: start_id='${g.start_id}' (index ${s}) > end_id='${g.end_id}' (index ${e})`\n\t\t\t\t)\n\t\t\t}\n\t\t\tlevelGroups.push(g)","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/graph/flowStructure.ts#L73-L109","documentation":"buildStructureTreeRecurse also rejects groups whose start_id or end_id is a virtual node id (Input, Result, Trigger). Virtual nodes are graph scaffolding, not real flow modules, so they cannot be members of a branch group.","triggerScenarios":"Creating a group from a graph selection that includes the Input, Result, or Trigger boundary nodes, then passing it to buildStructureTree via buildStructureTreeRecurse.","commonSituations":"Select-all in a flow graph editor capturing boundary nodes; drag-select rectangles that overlap virtual node handles; programmatic group creation using edge endpoints that include virtual nodes.","solutions":["Filter virtual node ids (VIRTUAL_NODE_IDS) out of selections before constructing groups.","When creating groups from edges, skip edges incident to Input/Result/Trigger nodes.","Sanitize stored groups on load by dropping any group referencing virtual nodes."],"exampleFix":"// before\nconst group = { id, start_id: sel[0].id, end_id: sel[1].id }\n// after\nconst real = sel.filter((n) => !VIRTUAL_NODE_IDS.has(n.id))\nif (real.length !== sel.length) return // or re-select\nconst group = { id, start_id: real[0].id, end_id: real[real.length - 1].id }","handlingStrategy":"validation","validationCode":"const safeGroups = groups.filter(\n  (g) => !VIRTUAL_NODE_IDS.has(g.start_id) && !VIRTUAL_NODE_IDS.has(g.end_id)\n)\nconst tree = buildStructureTree(modules, safeGroups)","typeGuard":"function groupsExcludeVirtualNodes(groups: GraphGroup[]): boolean {\n  return groups.every((g) => !VIRTUAL_NODE_IDS.has(g.start_id) && !VIRTUAL_NODE_IDS.has(g.end_id))\n}","tryCatchPattern":"try {\n  const tree = buildStructureTree(modules, groups)\n} catch (e) {\n  if (e.message.includes('virtual node')) {\n    return buildStructureTree(modules, groups.filter(nonVirtual))\n  }\n  throw e\n}","preventionTips":["Filter VIRTUAL_NODE_IDS out of graph selections before group creation.","Skip edges incident to Input/Result/Trigger when deriving groups from edges.","Sanitize persisted groups on load by dropping virtual-node references."],"tags":["svelte","flow-graph","invalid-node"],"backgroundTag":"invalid-node-reference","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}