gastownhall/beads · error
adding edge %s->%s: %w
Error message
adding edge %s->%s: %w
What it means
This wraps a failure from tx.AddDependencyWithOptions when persisting a graph edge dependency inside the apply transaction. The edge constructed successfully but the storage layer refused the insert — commonly a foreign-key violation (endpoint issue missing in the DB), a duplicate dependency, or a backend error — aborting the whole graph apply.
Source
Thrown at cmd/bd/graph_apply.go:1055
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 {
continue
}
d, err := types.NewGraphNodeDependency(issues[i].ID, depType, dep.Target, keyToID)
if err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped error for the storage-level cause (foreign key, duplicate, connection)
- Re-run bd graph apply after confirming both endpoint issues exist (bd show)
- Deduplicate the plan so the same edge isn't inserted twice
- Verify database health (bd doctor) and retry on a clean connection
Example fix
// before: applying a stale plan after the target issue was deleted bd graph apply plan.json // after: regenerate the plan and ensure targets exist bd show task-2 && bd graph apply plan.json
Defensive patterns
Strategy: try-catch
Validate before calling
for _, e := range plan.Edges {
if !issueExists(resolve(e.From)) || !issueExists(resolve(e.To)) {
return fmt.Errorf("edge endpoint missing in db")
}
} Type guard
func depInsertable(e Edge, keyToID map[string]string, db DB) bool {
return db.HasIssue(keyToID[e.FromKey]) && db.HasIssue(keyToID[e.ToKey])
} Try / catch
if err := bd.GraphApply(ctx, plan); err != nil {
if strings.Contains(err.Error(), "adding edge") {
// transaction rolled back; safe to retry after fixing storage
return retryGraphApply(ctx, plan)
}
return err
} Prevention
- Don't run concurrent writers against the same beads database
- Regenerate plans after any issue deletions
- Run bd doctor to verify storage health before large applies
When it happens
Trigger: tx.AddDependencyWithOptions(ctx, dep, actor, storage.DependencyAddOptions{}) returns an error after NewGraphEdgeDependency succeeded, e.g. fromID/toID exists in keyToID but the corresponding row is missing in storage, or a uniqueness/constraint violation.
Common situations: Concurrent modifications racing with the apply (issue deleted between planning and apply), database constraint conflicts from a partially-applied earlier attempt, or storage connectivity problems.
Related errors
- node %q: adding parent-child dep: %w
- node %q: adding dep to %q: %w
- final graph cycle check: %w
- node %q: setting assignee: %w
- db: DependencySQLRepository.Insert: dep must not be nil
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/1ad4376138218e77.
Report an issue: GitHub.