gastownhall/beads · error
edge %d: must specify from_key or from_id
Error message
edge %d: must specify from_key or from_id
What it means
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.
Source
Thrown at cmd/bd/graph_apply.go:600
}
if dep.Type != "" {
dt := types.DependencyType(dep.Type)
if !dt.IsValid() {
return fmt.Errorf("node %q: dep %d: invalid dependency type %q", node.Key, j, dep.Type)
}
}
}
}
for i, edge := range plan.Edges {
if edge.FromKey != "" && !seenKeys[edge.FromKey] {
return fmt.Errorf("edge %d: from key %q not found in plan", i, edge.FromKey)
}
if edge.ToKey != "" && !seenKeys[edge.ToKey] {
return fmt.Errorf("edge %d: to key %q not found in plan", i, edge.ToKey)
}
if edge.FromKey == "" && edge.FromID == "" {
return fmt.Errorf("edge %d: must specify from_key or from_id", i)
}
if edge.ToKey == "" && edge.ToID == "" {
return fmt.Errorf("edge %d: must specify to_key or to_id", i)
}
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 != "" {View on GitHub (pinned to 71377f2769)
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
Example fix
// before
{"edges": [{"from_key": "", "from_id": "", "to_key": "deploy"}]}
// after
{"edges": [{"from_key": "build", "to_key": "deploy"}]} Defensive patterns
Strategy: validation
Validate before calling
for i, e := range plan.Edges {
if e.FromKey == "" && e.FromID == "" { return fmt.Errorf("edge %d: missing source", i) }
if e.ToKey == "" && e.ToID == "" { return fmt.Errorf("edge %d: missing target", i) }
} Try / catch
if err := bd.GraphApply(ctx, plan, opts); err != nil {
if strings.Contains(err.Error(), "must specify from_key or from_id") {
// add a source to the edge and retry
}
} Prevention
- 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
When it happens
Trigger: `bd graph apply` with plan.edges[i] that has neither from_key nor from_id set.
Common situations: Generated edge entries with placeholder/empty source fields; hand-written edges copying only the target side; destructuring errors in scripts building edges.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- node %q has empty title
- node %q: dep %d has empty target
- duplicate node key %q
- duplicate explicit id %q (node %q)
- node %q: metadata ref %q references unknown key %q
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/12c4d4c92a36c918.
Report an issue: GitHub.