windmill-labs/windmill · error

Group(s) ${ids} could not be resolved: their start/end nodes

Error message

Group(s) ${ids} could not be resolved: their start/end nodes do not belong to the same branch

What it means

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.

Source

Thrown at frontend/src/lib/components/graph/flowStructure.ts:43

	moduleIds?: string[]
	/** Child branches. leaf=[], group=[{children}], container=[{children}, ...] */
	branches: StructureBranch[]
}

// ---------------------------------------------------------------------------
// Type guards
// ---------------------------------------------------------------------------
// Building the structure tree
// ---------------------------------------------------------------------------

export function buildStructureTree(
	modules: FlowModule[],
	groups: GraphGroup[]
): FlowStructureNode[] {
	const { items, consumed } = buildStructureTreeRecurse(modules, groups)
	const unconsumed = groups.filter((g) => !consumed.has(g.id))
	if (unconsumed.length > 0) {
		throw new Error(
			`Group(s) ${unconsumed.map((g) => `'${g.id}'`).join(', ')} could not be resolved: ` +
				`their start/end nodes do not belong to the same branch`
		)
	}
	return items
}

export function moduleToStructureNode(mod: FlowModule): FlowStructureNode {
	const innerArrays = getContainerInnerArrays(mod)
	if (innerArrays.length === 0) {
		return { id: mod.id, kind: 'leaf', branches: [] }
	}

	const kind = (mod.value as any).type as ContainerKind
	const branches: StructureBranch[] = innerArrays.map(({ get, label }) => ({
		label,
		children: [] // filled later by recursion
	}))

View on GitHub (pinned to e474e8803c)

Solutions

  1. Recompute groups from the current module structure before calling; discard groups referencing removed node ids.
  2. Ensure each group's start_id and end_id belong to the same branch when creating selections.
  3. Validate groups against the current graph and drop/filter invalid ones instead of passing them through.

Example fix

// before
const tree = buildStructureTree(modules, groups)
// after
const validIds = new Set(modules.map((m) => m.id))
const safeGroups = groups.filter(
  (g) => validIds.has(g.start_id) && validIds.has(g.end_id)
)
const tree = buildStructureTree(modules, safeGroups)
Defensive patterns

Strategy: validation

Validate before calling

const ids = new Set(modules.map((m) => m.id))
const safeGroups = groups.filter((g) => ids.has(g.start_id) && ids.has(g.end_id))
const tree = buildStructureTree(modules, safeGroups)

Type guard

function groupsReferenceKnownNodes(modules: FlowModule[], groups: GraphGroup[]): boolean {
  const ids = new Set(modules.map((m) => m.id))
  return groups.every((g) => ids.has(g.start_id) && ids.has(g.end_id))
}

Try / catch

try {
  const tree = buildStructureTree(modules, groups)
} catch (e) {
  if (e.message.includes('could not be resolved')) {
    groups = recomputeGroupsFromStructure(modules)
    return buildStructureTree(modules, groups)
  }
  throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/282dec2dc87b5a19. Report an issue: GitHub.