{"record":{"id":"4239ce6ab0acef1a","repo":"gastownhall/beads","slug":"edge-d-spawner-key-q-cannot-be-combined-with-to","errorCode":null,"errorMessage":"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)","messagePattern":"edge (.+?): spawner_key %q cannot be combined with to_id %q \\(to_id overrides to_key as the waits-for target; use spawner_id\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/graph_apply.go","lineNumber":629,"sourceCode":"\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 {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// validateGraphApplyNodeFields checks the single-node fields added for","sourceCodeStart":611,"sourceCodeEnd":647,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/graph_apply.go#L611-L647","documentation":"Gate evaluation reads the spawner from the dependency target (depends_on_id), and to_id overrides to_key when resolving the edge's to endpoint. Combining a key-based spawner with a to_id would make the spawner and target diverge, so the combination is rejected; use spawner_id instead.","triggerScenarios":"An edge sets both spawner_key and to_id. Even if spawner_key matches to_key, apply resolves the target via to_id, so the key-based spawner would be wrong.","commonSituations":"Plans where a tool added to_id for external references while other edges retain key-based spawners; mixing ID-addressed and key-addressed edges in one waits-for family.","solutions":["Replace spawner_key with spawner_id matching to_id","Or drop to_id and address the to endpoint by to_key that equals spawner_key"],"exampleFix":"// before\n{\"type\": \"waits-for\", \"spawner_key\": \"parent\", \"to_id\": \"bd-42\"}\n// after\n{\"type\": \"waits-for\", \"spawner_id\": \"bd-42\", \"to_id\": \"bd-42\"}","handlingStrategy":"validation","validationCode":"if e.SpawnerKey != \"\" && e.ToID != \"\" {\n  return fmt.Errorf(\"edge: use spawner_id when the to endpoint is addressed by to_id\")\n}","typeGuard":"func spawnerCompat(e Edge) bool { return !(e.SpawnerKey != \"\" && e.ToID != \"\") }","tryCatchPattern":null,"preventionTips":["Keep one addressing style (keys or IDs) per edge","Remember to_id wins over to_key at apply time","When enriching key plans with IDs, convert spawner_key to spawner_id too"],"tags":["graph-plan","waits-for","id-key-conflict"],"backgroundTag":"spawner-target-mismatch","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}