windmill-labs/windmill · error

Groups '${a.id}' and '${b.id}' overlap without nesting

Error message

Groups '${a.id}' and '${b.id}' overlap without nesting

What it means

Two sibling groups at the same level must be either disjoint or strictly nested. This throw fires when two groups partially overlap (each has nodes the other lacks), which cannot be expressed as a hierarchy of nested regions in the graph.

Source

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

			otherGroups.push(g)
		}
	}

	// Validate no partial overlaps
	for (let i = 0; i < levelGroups.length; i++) {
		for (let j = i + 1; j < levelGroups.length; j++) {
			const a = levelGroups[i]
			const b = levelGroups[j]
			const aStart = indexMap.get(a.start_id)!
			const aEnd = indexMap.get(a.end_id)!
			const bStart = indexMap.get(b.start_id)!
			const bEnd = indexMap.get(b.end_id)!

			if (aEnd < bStart || bEnd < aStart) continue
			if (aStart <= bStart && bEnd <= aEnd) continue
			if (bStart <= aStart && aEnd <= bEnd) continue

			throw new Error(`Groups '${a.id}' and '${b.id}' overlap without nesting`)
		}
	}

	// Build grouped structure for this level
	function build(
		startIdx: number,
		endIdx: number,
		availableGroups: GraphGroup[]
	): FlowStructureNode[] {
		const result: FlowStructureNode[] = []
		let i = startIdx
		while (i <= endIdx) {
			const candidates = availableGroups.filter((g) => {
				const gStart = indexMap.get(g.start_id)!
				const gEnd = indexMap.get(g.end_id)!
				return gStart === i && gEnd <= endIdx
			})
			candidates.sort((a, b) => {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Open the flow and adjust the two groups so one fully contains the other, or shrink them so they no longer overlap.
  2. Delete and redraw the conflicting group(s) in the editor to regenerate consistent boundaries.
  3. Compare the deployed flow JSON groups and fix the overlapping start_id/end_id pairs manually.
  4. If a merge introduced it, redo the merge keeping group definitions from one side only.

Example fix

// before
groups: [{ id: 'a', start_id: 's1', end_id: 's3' }, { id: 'b', start_id: 's2', end_id: 's5' }]
// after
groups: [{ id: 'a', start_id: 's1', end_id: 's5' }] // b nested or removed
Defensive patterns

Strategy: validation

Validate before calling

for (let i = 0; i < groups.length; i++) for (let j = i + 1; j < groups.length; j++) {
  const a = groups[i], b = groups[j]
  const nested = (as <= bs && be <= ae) || (bs <= as && ae <= be)
  const disjoint = ae < bs || be < as
  if (!nested && !disjoint) console.warn('overlap', a.id, b.id)
}

Type guard

function groupsAreHierarchical(groups, indexMap) {
  return groups.every((a, i) => groups.every((b, j) => i >= j || (fn(indexMap, a, b) || fn(indexMap, b, a))))
}
const fn = (m, a, b) => { const as=m.get(a.start_id), ae=m.get(a.end_id), bs=m.get(b.start_id), be=m.get(b.end_id); return ae<bs || be<as || (as<=bs && be<=ae) }

Try / catch

try { tree = buildStructureTree(items, groups) } catch (e) { if (String(e.message).includes('overlap without nesting')) showGroupConflictDialog(e); else throw e }

Prevention

When it happens

Trigger: Two GraphGroups a and b where ranges [aStart,aEnd] and [bStart,bEnd] intersect but neither contains the other — e.g. group1 covers steps 1-3 and group2 covers steps 2-5.

Common situations: Editing a flow and dragging a step into/out of a branch while stale group boundaries persist; duplicated group ids with shifted boundaries after a merge; imported flow JSON with hand-drawn group boxes that cross each other.

Related errors


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