{"record":{"id":"12c4d4c92a36c918","repo":"gastownhall/beads","slug":"edge-d-must-specify-from-key-or-from-id","errorCode":null,"errorMessage":"edge %d: must specify from_key or from_id","messagePattern":"edge (.+?): must specify from_key or from_id","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/graph_apply.go","lineNumber":600,"sourceCode":"\t\t\t}\n\t\t\tif dep.Type != \"\" {\n\t\t\t\tdt := types.DependencyType(dep.Type)\n\t\t\t\tif !dt.IsValid() {\n\t\t\t\t\treturn fmt.Errorf(\"node %q: dep %d: invalid dependency type %q\", node.Key, j, dep.Type)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor i, edge := range plan.Edges {\n\t\tif edge.FromKey != \"\" && !seenKeys[edge.FromKey] {\n\t\t\treturn fmt.Errorf(\"edge %d: from key %q not found in plan\", i, edge.FromKey)\n\t\t}\n\t\tif edge.ToKey != \"\" && !seenKeys[edge.ToKey] {\n\t\t\treturn fmt.Errorf(\"edge %d: to key %q not found in plan\", i, edge.ToKey)\n\t\t}\n\t\tif edge.FromKey == \"\" && edge.FromID == \"\" {\n\t\t\treturn fmt.Errorf(\"edge %d: must specify from_key or from_id\", i)\n\t\t}\n\t\tif edge.ToKey == \"\" && edge.ToID == \"\" {\n\t\t\treturn fmt.Errorf(\"edge %d: must specify to_key or to_id\", i)\n\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 != \"\" {","sourceCodeStart":582,"sourceCodeEnd":618,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/graph_apply.go#L582-L618","documentation":"Every plan edge must identify its source, either via from_key or from_id. An edge with both empty cannot be anchored and validation rejects it with the edge index.","triggerScenarios":"`bd graph apply` with plan.edges[i] that has neither from_key nor from_id set.","commonSituations":"Generated edge entries with placeholder/empty source fields; hand-written edges copying only the target side; destructuring errors in scripts building edges.","solutions":["Set from_key to the source node's key in the plan","Alternatively set from_id to an explicit issue id","Remove the edge if it is spurious"],"exampleFix":"// before\n{\"edges\": [{\"from_key\": \"\", \"from_id\": \"\", \"to_key\": \"deploy\"}]}\n// after\n{\"edges\": [{\"from_key\": \"build\", \"to_key\": \"deploy\"}]}","handlingStrategy":"validation","validationCode":"for i, e := range plan.Edges {\n\tif e.FromKey == \"\" && e.FromID == \"\" { return fmt.Errorf(\"edge %d: missing source\", i) }\n\tif e.ToKey == \"\" && e.ToID == \"\" { return fmt.Errorf(\"edge %d: missing target\", i) }\n}","typeGuard":null,"tryCatchPattern":"if err := bd.GraphApply(ctx, plan, opts); err != nil {\n\tif strings.Contains(err.Error(), \"must specify from_key or from_id\") {\n\t\t// add a source to the edge and retry\n\t}\n}","preventionTips":["Use an edge constructor that requires both endpoints","Never emit edge objects with empty source fields","Schema-validate plan files to require from_key or from_id"],"tags":["validation","graph-apply","missing-field"],"backgroundTag":"missing-required-field","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}