gastownhall/beads · error
edge %d: spawner key %q not found in plan
Error message
edge %d: spawner key %q not found in plan
What it means
spawner_key must name a node defined in the same graph plan (checked against the seenKeys set). If it references a node that isn't part of the plan, gate evaluation would have nothing to resolve, so apply rejects the edge.
Source
Thrown at cmd/bd/graph_apply.go:622
}
if edge.Type != "" {
dt := types.DependencyType(edge.Type)
if !dt.IsValid() {
return fmt.Errorf("edge %d: invalid dependency type %q", i, edge.Type)
}
}
if edge.Gate != "" || edge.SpawnerKey != "" || edge.SpawnerID != "" {
if graphApplyDependencyType(edge.Type) != types.DepWaitsFor {
return fmt.Errorf("edge %d: gate/spawner fields require type %q", i, types.DepWaitsFor)
}
if edge.Gate != "" && !types.IsValidWaitsForGate(edge.Gate) {
return fmt.Errorf("edge %d: invalid gate %q (valid: %s, %s)", i, edge.Gate, types.WaitsForAllChildren, types.WaitsForAnyChildren)
}
if edge.SpawnerKey != "" && edge.SpawnerID != "" {
return fmt.Errorf("edge %d: cannot specify both spawner_key and spawner_id", i)
}
if edge.SpawnerKey != "" && !seenKeys[edge.SpawnerKey] {
return fmt.Errorf("edge %d: spawner key %q not found in plan", i, edge.SpawnerKey)
}
// Gate evaluation reads the spawner from the dependency target
// (depends_on_id), not metadata, so the spawner must equal the to
// endpoint. Since to_id overrides to_key at apply time
// (resolveEdgeRef), a key-named spawner can't be combined with to_id.
if edge.SpawnerKey != "" && edge.ToID != "" {
return fmt.Errorf("edge %d: spawner_key %q cannot be combined with to_id %q (to_id overrides to_key as the waits-for target; use spawner_id)", i, edge.SpawnerKey, edge.ToID)
}
if edge.SpawnerKey != "" && edge.SpawnerKey != edge.ToKey {
return fmt.Errorf("edge %d: spawner_key %q must match to_key %q (the waits-for target is the spawner)", i, edge.SpawnerKey, edge.ToKey)
}
if edge.SpawnerID != "" && edge.SpawnerID != edge.ToID {
return fmt.Errorf("edge %d: spawner_id %q must match to_id %q (the waits-for target is the spawner)", i, edge.SpawnerID, edge.ToID)
}
}
}
if err := validateGraphApplyLocalCycles(plan, seenKeys); err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Add the spawner node to the plan, or change spawner_key to an existing node key in this plan
- Use spawner_id with to_id instead if the spawner is an external issue
- Fix typos so spawner_key exactly matches a node key
Example fix
// before
{"nodes": [{"key": "child"}], "edges": [{"type": "waits-for", "spawner_key": "parent", "to_key": "child"}]}
// after
{"nodes": [{"key": "child"},{"key": "parent"}], "edges": [{"type": "waits-for", "spawner_key": "parent", "to_key": "child"}]} Defensive patterns
Strategy: validation
Validate before calling
seen := map[string]bool{}
for _, n := range plan.Nodes { seen[n.Key] = true }
for _, e := range plan.Edges {
if e.SpawnerKey != "" && !seen[e.SpawnerKey] { return fmt.Errorf("spawner_key %q not in plan", e.SpawnerKey) }
} Type guard
func spawnerInPlan(e Edge, keys map[string]bool) bool { return e.SpawnerKey == "" || keys[e.SpawnerKey] } Prevention
- Reference only node keys defined in the same plan
- Regenerate plans wholesale instead of copying edges between plans
- Check spawner references after renaming node keys
When it happens
Trigger: An edge references spawner_key: "xyz" where "xyz" is not any node's key in this plan — e.g. referencing an external issue's key or a typo.
Common situations: Copying an edge from another plan; assuming spawner_key accepts issue IDs; renaming a node key without updating spawner references.
Related errors
- edge %d: gate/spawner fields require type %q
- edge %d: invalid gate %q (valid: %s, %s)
- edge %d: cannot specify both spawner_key and spawner_id
- edge %d: spawner_key %q cannot be combined with to_id %q (to
- edge %d: spawner_key %q must match to_key %q (the waits-for
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/e18cf312c306539c.
Report an issue: GitHub.