gastownhall/beads · error
duplicate step IDs after expansion: %v
Error message
duplicate step IDs after expansion: %v
What it means
After all expand/map rules are applied, ApplyExpansions runs findDuplicateStepIDs over the final step list. If two or more steps share an ID, the result would be ambiguous, so it fails with the list of duplicated IDs.
Source
Thrown at internal/formula/expand.go:160
result = replaceStep(result, targetStep.ID, expandedSteps)
expanded[targetStep.ID] = true
// Update dependencies: any step that depended on the target should now
// depend on the last step of the expansion
if len(expandedSteps) > 0 {
lastStepID := expandedSteps[len(expandedSteps)-1].ID
result = UpdateDependenciesForExpansion(result, targetStep.ID, lastStepID)
}
// Rebuild stepMap from result so subsequent iterations see resolved deps
stepMap = buildStepMap(result)
}
}
// Validate no duplicate step IDs after expansion
if dups := findDuplicateStepIDs(result); len(dups) > 0 {
return nil, fmt.Errorf("duplicate step IDs after expansion: %v", dups)
}
return result, nil
}
// findDuplicateStepIDs returns any duplicate step IDs found in the steps slice.
// It recursively checks all children.
func findDuplicateStepIDs(steps []*Step) []string {
seen := make(map[string]int)
countStepIDs(steps, seen)
var dups []string
for id, count := range seen {
if count > 1 {
dups = append(dups, id)
}
}
return dupsView on GitHub (pinned to 71377f2769)
Solutions
- Make template step IDs unique, e.g. include ${parent} or an input variable in the id
- Narrow map select patterns so no step is expanded by two rules
- Rename static steps that collide with template-generated IDs
Example fix
# before
steps:
- id: "child" # collides for every expansion
# after
steps:
- id: "${parent}-child" Defensive patterns
Strategy: validation
Validate before calling
ids := map[string]int{}
for _, s := range expandedStepsLocalSimulation { ids[s.ID]++ }
for id, n := range ids {
if n > 1 { return fmt.Errorf("would produce duplicate step id %q", id) }
} Try / catch
result, err := formula.ApplyExpansions(steps, compose)
if err != nil && strings.Contains(err.Error(), "duplicate step IDs") {
return fmt.Errorf("fix template id variables to guarantee uniqueness: %w", err)
} Prevention
- Build template step IDs from unique inputs, e.g. ${parent}-${i}
- Ensure map select patterns don't overlap
- Add a pre-commit check simulating expansion ID generation
When it happens
Trigger: ApplyExpansions final validation finds duplicates, typically because two map rules expanded steps into templates producing the same generated ID, or an expansion template produced IDs colliding with existing steps.
Common situations: Template IDs built from variables that resolve to the same value for different inputs; overlapping map select patterns expanding the same step twice; hardcoded template IDs colliding with static step IDs.
Related errors
- applying expansions: %w
- no store is open for this workspace
- not found
- no absolute native user directory is available
- ExternalDoltConfig: set either Socket OR (Host, Port), not b
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/7031181815aaacda.
Report an issue: GitHub.