gastownhall/beads · error
edge %s->%s: %w
Error message
edge %s->%s: %w
What it means
This wraps an error returned by types.NewGraphEdgeDependency while constructing the Dependency record for a plan edge. Construction validates that edge endpoints resolve to real issue IDs (via keyToID) and that gate/spawner/thread metadata is consistent; a failure here means the edge references an unknown node key or has invalid attributes, so the apply transaction stops.
Source
Thrown at cmd/bd/graph_apply.go:1052
for i, edge := range plan.Edges {
fromID := resolveEdgeRef(edge.FromKey, edge.FromID, keyToID)
toID := resolveEdgeRef(edge.ToKey, edge.ToID, keyToID)
depType := graphApplyDependencyType(edge.Type)
if (depType == types.DepParentChild) != parentPhase {
continue
}
if parentDepPairs[graphApplyDepPairKey(fromID, toID)] {
if depType == types.DepParentChild {
continue
}
return fmt.Errorf("edge %d %s->%s duplicates a parent-child relationship with dependency type %q", i, fromID, toID, depType)
}
if parentDepPairs[graphApplyDepPairKey(toID, fromID)] && graphApplyCycleRelevantDependencyType(depType) {
return fmt.Errorf("edge %d %s->%s creates a blocking reverse of a parent-child relationship", i, fromID, toID)
}
dep, err := types.NewGraphEdgeDependency(fromID, toID, depType, edge.Gate, edge.SpawnerKey, edge.SpawnerID, edge.ThreadID, keyToID)
if err != nil {
return fmt.Errorf("edge %s->%s: %w", fromID, toID, err)
}
if err := tx.AddDependencyWithOptions(ctx, dep, actor, storage.DependencyAddOptions{}); err != nil {
return fmt.Errorf("adding edge %s->%s: %w", fromID, toID, err)
}
if graphApplySchedulingDependencyType(depType) {
newSchedulingEdges = append(newSchedulingEdges, [2]string{fromID, toID})
}
}
// Add per-node inline dependencies in stable order for this phase.
for i, node := range plan.Nodes {
for _, dep := range node.Deps {
depType := types.DependencyType(dep.Type)
if depType == "" {
depType = types.DepBlocks
}
if (depType == types.DepParentChild) != parentPhase {
continueView on GitHub (pinned to 71377f2769)
Solutions
- Check that both fromKey/toKey exist in the plan's nodes or as existing issue keys in the database
- Fix typos in edge endpoint keys in the graph JSON
- Remove or correct gate/spawner/thread fields that reference unknown nodes
- Run bd doctor / db integrity checks if keys appear valid but don't resolve
Example fix
// before
{"from":"tsk-1","to":"task-1","type":"blocks"} // typo: tsk-1
// after
{"from":"task-1","to":"task-2","type":"blocks"} Defensive patterns
Strategy: validation
Validate before calling
for _, e := range plan.Edges {
if keys[e.FromKey] == nil && !existsInDB(e.FromKey) { return fmt.Errorf("unknown edge endpoint %s", e.FromKey) }
if keys[e.ToKey] == nil && !existsInDB(e.ToKey) { return fmt.Errorf("unknown edge endpoint %s", e.ToKey) }
} Type guard
func endpointsResolve(e Edge, keyToID map[string]string) bool {
return keyToID[e.FromKey] != "" && keyToID[e.ToKey] != ""
} Try / catch
if err := bd.GraphApply(ctx, plan); err != nil && strings.Contains(err.Error(), "edge ") {
return fmt.Errorf("plan has unresolved edge endpoints: %w", err)
} Prevention
- Lint all edge keys against the plan's node keys before applying
- Avoid hand-editing generated graph JSON; regenerate instead
- Keep gate/spawner references pointing at nodes present in the plan
When it happens
Trigger: plan.Edges contains fromKey/toKey values not present in keyToID (nodes not created in this plan nor existing in the DB), or invalid gate/spawner references passed to NewGraphEdgeDependency.
Common situations: Typos in edge endpoint keys in graph JSON, referencing an external issue key that was never imported, or spawner/gate metadata pointing at nodes not in the plan.
Related errors
- node %q: adding parent-child dep: %w
- adding edge %s->%s: %w
- node %q: adding dep to %q: %w
- final graph cycle check: %w
- graph dependency cycle would be created: %s
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/3b4a910f9ad6a03a.
Report an issue: GitHub.