{"record":{"id":"e18cf312c306539c","repo":"gastownhall/beads","slug":"edge-d-spawner-key-q-not-found-in-plan","errorCode":null,"errorMessage":"edge %d: spawner key %q not found in plan","messagePattern":"edge (.+?): spawner key %q not found in plan","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/graph_apply.go","lineNumber":622,"sourceCode":"\t\t}\n\t\tif edge.Type != \"\" {\n\t\t\tdt := types.DependencyType(edge.Type)\n\t\t\tif !dt.IsValid() {\n\t\t\t\treturn fmt.Errorf(\"edge %d: invalid dependency type %q\", i, edge.Type)\n\t\t\t}\n\t\t}\n\t\tif edge.Gate != \"\" || edge.SpawnerKey != \"\" || edge.SpawnerID != \"\" {\n\t\t\tif graphApplyDependencyType(edge.Type) != types.DepWaitsFor {\n\t\t\t\treturn fmt.Errorf(\"edge %d: gate/spawner fields require type %q\", i, types.DepWaitsFor)\n\t\t\t}\n\t\t\tif edge.Gate != \"\" && !types.IsValidWaitsForGate(edge.Gate) {\n\t\t\t\treturn fmt.Errorf(\"edge %d: invalid gate %q (valid: %s, %s)\", i, edge.Gate, types.WaitsForAllChildren, types.WaitsForAnyChildren)\n\t\t\t}\n\t\t\tif edge.SpawnerKey != \"\" && edge.SpawnerID != \"\" {\n\t\t\t\treturn fmt.Errorf(\"edge %d: cannot specify both spawner_key and spawner_id\", i)\n\t\t\t}\n\t\t\tif edge.SpawnerKey != \"\" && !seenKeys[edge.SpawnerKey] {\n\t\t\t\treturn fmt.Errorf(\"edge %d: spawner key %q not found in plan\", i, edge.SpawnerKey)\n\t\t\t}\n\t\t\t// Gate evaluation reads the spawner from the dependency target\n\t\t\t// (depends_on_id), not metadata, so the spawner must equal the to\n\t\t\t// endpoint. Since to_id overrides to_key at apply time\n\t\t\t// (resolveEdgeRef), a key-named spawner can't be combined with to_id.\n\t\t\tif edge.SpawnerKey != \"\" && edge.ToID != \"\" {\n\t\t\t\treturn 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)\n\t\t\t}\n\t\t\tif edge.SpawnerKey != \"\" && edge.SpawnerKey != edge.ToKey {\n\t\t\t\treturn fmt.Errorf(\"edge %d: spawner_key %q must match to_key %q (the waits-for target is the spawner)\", i, edge.SpawnerKey, edge.ToKey)\n\t\t\t}\n\t\t\tif edge.SpawnerID != \"\" && edge.SpawnerID != edge.ToID {\n\t\t\t\treturn fmt.Errorf(\"edge %d: spawner_id %q must match to_id %q (the waits-for target is the spawner)\", i, edge.SpawnerID, edge.ToID)\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := validateGraphApplyLocalCycles(plan, seenKeys); err != nil {","sourceCodeStart":604,"sourceCodeEnd":640,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/graph_apply.go#L604-L640","documentation":"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.","triggerScenarios":"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.","commonSituations":"Copying an edge from another plan; assuming spawner_key accepts issue IDs; renaming a node key without updating spawner references.","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"],"exampleFix":"// before\n{\"nodes\": [{\"key\": \"child\"}], \"edges\": [{\"type\": \"waits-for\", \"spawner_key\": \"parent\", \"to_key\": \"child\"}]}\n// after\n{\"nodes\": [{\"key\": \"child\"},{\"key\": \"parent\"}], \"edges\": [{\"type\": \"waits-for\", \"spawner_key\": \"parent\", \"to_key\": \"child\"}]}","handlingStrategy":"validation","validationCode":"seen := map[string]bool{}\nfor _, n := range plan.Nodes { seen[n.Key] = true }\nfor _, e := range plan.Edges {\n  if e.SpawnerKey != \"\" && !seen[e.SpawnerKey] { return fmt.Errorf(\"spawner_key %q not in plan\", e.SpawnerKey) }\n}","typeGuard":"func spawnerInPlan(e Edge, keys map[string]bool) bool { return e.SpawnerKey == \"\" || keys[e.SpawnerKey] }","tryCatchPattern":null,"preventionTips":["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"],"tags":["graph-plan","waits-for","reference-resolution"],"backgroundTag":"plan-key-not-found","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}