{"record":{"id":"282dec2dc87b5a19","repo":"windmill-labs/windmill","slug":"group-s-ids-could-not-be-resolved-their-start","errorCode":null,"errorMessage":"Group(s) ${ids} could not be resolved: their start/end nodes do not belong to the same branch","messagePattern":"Group\\(s\\) (.+?) could not be resolved: their start/end nodes do not belong to the same branch","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/graph/flowStructure.ts","lineNumber":43,"sourceCode":"\tmoduleIds?: string[]\n\t/** Child branches. leaf=[], group=[{children}], container=[{children}, ...] */\n\tbranches: StructureBranch[]\n}\n\n// ---------------------------------------------------------------------------\n// Type guards\n// ---------------------------------------------------------------------------\n// Building the structure tree\n// ---------------------------------------------------------------------------\n\nexport function buildStructureTree(\n\tmodules: FlowModule[],\n\tgroups: GraphGroup[]\n): FlowStructureNode[] {\n\tconst { items, consumed } = buildStructureTreeRecurse(modules, groups)\n\tconst unconsumed = groups.filter((g) => !consumed.has(g.id))\n\tif (unconsumed.length > 0) {\n\t\tthrow new Error(\n\t\t\t`Group(s) ${unconsumed.map((g) => `'${g.id}'`).join(', ')} could not be resolved: ` +\n\t\t\t\t`their start/end nodes do not belong to the same branch`\n\t\t)\n\t}\n\treturn items\n}\n\nexport function moduleToStructureNode(mod: FlowModule): FlowStructureNode {\n\tconst innerArrays = getContainerInnerArrays(mod)\n\tif (innerArrays.length === 0) {\n\t\treturn { id: mod.id, kind: 'leaf', branches: [] }\n\t}\n\n\tconst kind = (mod.value as any).type as ContainerKind\n\tconst branches: StructureBranch[] = innerArrays.map(({ get, label }) => ({\n\t\tlabel,\n\t\tchildren: [] // filled later by recursion\n\t}))","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/graph/flowStructure.ts#L25-L61","documentation":"buildStructureTree converts flat flow modules plus GraphGroups (branch groupings) into a nested flow structure tree. After recursion, any group that was not consumed by buildStructureTreeRecurse could not be matched to a branch — its start_id and end_id don't sit within the same branch — so it throws with the offending group ids listed.","triggerScenarios":"Calling buildStructureTree with groups whose start_id/end_id span different branches (e.g., a group starting in the 'then' branch and ending in the 'else' branch of a branching step), or referencing ids that no longer exist after modules were edited/removed.","commonSituations":"Stale saved groups after deleting or restructuring branch steps; cross-branch selections made in a custom flow-graph editor; migrations or imported flows carrying group definitions from an older structure.","solutions":["Recompute groups from the current module structure before calling; discard groups referencing removed node ids.","Ensure each group's start_id and end_id belong to the same branch when creating selections.","Validate groups against the current graph and drop/filter invalid ones instead of passing them through."],"exampleFix":"// before\nconst tree = buildStructureTree(modules, groups)\n// after\nconst validIds = new Set(modules.map((m) => m.id))\nconst safeGroups = groups.filter(\n  (g) => validIds.has(g.start_id) && validIds.has(g.end_id)\n)\nconst tree = buildStructureTree(modules, safeGroups)","handlingStrategy":"validation","validationCode":"const ids = new Set(modules.map((m) => m.id))\nconst safeGroups = groups.filter((g) => ids.has(g.start_id) && ids.has(g.end_id))\nconst tree = buildStructureTree(modules, safeGroups)","typeGuard":"function groupsReferenceKnownNodes(modules: FlowModule[], groups: GraphGroup[]): boolean {\n  const ids = new Set(modules.map((m) => m.id))\n  return groups.every((g) => ids.has(g.start_id) && ids.has(g.end_id))\n}","tryCatchPattern":"try {\n  const tree = buildStructureTree(modules, groups)\n} catch (e) {\n  if (e.message.includes('could not be resolved')) {\n    groups = recomputeGroupsFromStructure(modules)\n    return buildStructureTree(modules, groups)\n  }\n  throw e\n}","preventionTips":["Recompute groups whenever modules are added/removed/restructured.","Restrict group creation to selections within a single branch.","Purge groups referencing deleted node ids before persisting flow state."],"tags":["svelte","flow-graph","inconsistent-state"],"backgroundTag":"inconsistent-graph-state","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"}