windmill-labs/windmill · error
Duplicate group id: '${g.id}'
Error message
Duplicate group id: '${g.id}' What it means
buildStructureTreeRecurse validates the group list before partitioning nodes, rejecting duplicate group ids because groups are consumed and tracked by id — duplicates would corrupt the consumption bookkeeping and produce ambiguous structure trees.
Source
Thrown at frontend/src/lib/components/graph/flowStructure.ts:83
function buildStructureTreeRecurse(
modules: FlowModule[],
groups: GraphGroup[]
): { items: FlowStructureNode[]; consumed: Set<string> } {
if (modules.length === 0) {
return { items: [], consumed: new Set() }
}
const indexMap = new Map<string, number>()
for (let i = 0; i < modules.length; i++) {
indexMap.set(modules[i].id, i)
}
// Reject duplicate group IDs
const seenGroupIds = new Set<string>()
for (const g of groups) {
if (seenGroupIds.has(g.id)) {
throw new Error(`Duplicate group id: '${g.id}'`)
}
seenGroupIds.add(g.id)
}
// Reject groups referencing virtual nodes
for (const g of groups) {
if (VIRTUAL_NODE_IDS.has(g.start_id) || VIRTUAL_NODE_IDS.has(g.end_id)) {
throw new Error(
`Group '${g.id}' references virtual node: groups cannot include Input, Result, or Trigger`
)
}
}
// Partition: groups for this level vs rest
const levelGroups: GraphGroup[] = []
const otherGroups: GraphGroup[] = []
for (const g of groups) {
if (indexMap.has(g.start_id) && indexMap.has(g.end_id)) {View on GitHub (pinned to e474e8803c)
Solutions
- Deduplicate groups by id before calling (e.g., new Map(groups.map(g => [g.id, g])).values()).
- Replace existing groups with the same id on selection instead of appending.
- Ensure group ids are generated from a reliable unique source (crypto.randomUUID or equivalent).
Example fix
// before const tree = buildStructureTree(modules, [...savedGroups, newGroup]) // after const merged = new Map([...savedGroups, newGroup].map((g) => [g.id, g])) const tree = buildStructureTree(modules, [...merged.values()])
Defensive patterns
Strategy: validation
Validate before calling
const uniqueGroups = [...new Map(groups.map((g) => [g.id, g])).values()] const tree = buildStructureTree(modules, uniqueGroups)
Type guard
function hasUniqueGroupIds(groups: GraphGroup[]): boolean {
return new Set(groups.map((g) => g.id)).size === groups.length
} Try / catch
try {
const tree = buildStructureTree(modules, groups)
} catch (e) {
if (e.message.includes('Duplicate group id')) {
return buildStructureTree(modules, dedupeById(groups))
}
throw e
} Prevention
- Merge group lists by id (upsert) instead of concatenating.
- Generate group ids with a reliable unique source.
- Deduplicate groups loaded from persisted state.
When it happens
Trigger: Passing a GraphGroup array containing two entries with the same id, e.g., from a buggy merge of group sets, a re-selection that appends instead of replaces, or data loaded twice from storage.
Common situations: Group list built by concatenating saved groups with newly created ones without deduplication; id-generation collisions from a faulty uuid source; importing flow state that already contains the group being added.
Related errors
- Group(s) ${ids} could not be resolved: their start/end nodes
- Group '${g.id}' references virtual node: groups cannot inclu
- This forked workspace '${workspaceId}' (${workspaceName}) al
- PlanWriteRefusedError
- Duplicate module ID found in preprocessor_module: ${preproce
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/c4c029bca8291e30.
Report an issue: GitHub.