gastownhall/beads · error
applyGraph: edge %d: %w
Error message
applyGraph: edge %d: %w
What it means
After all structural checks pass, applyGraph builds the dependency via types.NewGraphEdgeDependency (validating type/gate/spawner/thread references against keyToID) and inserts it. Any failure from the constructor is wrapped as "applyGraph: edge %d: %w" — this identifies the constructor stage, distinct from the storage-insert wrapper that includes the from->to IDs.
Source
Thrown at internal/storage/domain/issue.go:1261
depType = types.DepBlocks
}
if (depType == types.DepParentChild) != parentPhase {
continue
}
if parentDepPairs[depPairKey(fromID, toID)] {
if depType == types.DepParentChild {
continue
}
return GraphApplyResult{}, fmt.Errorf("applyGraph: edge %d %s->%s duplicates a parent-child relationship with dependency type %q", i, fromID, toID, depType)
}
if parentDepPairs[depPairKey(toID, fromID)] && cycleRelevantDepType(depType) {
return GraphApplyResult{}, fmt.Errorf("applyGraph: 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 GraphApplyResult{}, fmt.Errorf("applyGraph: edge %d: %w", i, err)
}
if err := u.depRepo.Insert(ctx, dep, actor, DepInsertOpts{UseWispsTable: useWisp}); err != nil {
return GraphApplyResult{}, fmt.Errorf("applyGraph: edge %d (%s -> %s): %w", i, fromID, toID, err)
}
if types.IsSchedulingEdge(depType) {
newSchedulingEdges = append(newSchedulingEdges, [2]string{fromID, toID})
}
}
// Per-node inline deps in stable order for this phase, resolved by the
// same shared builder as the embedded executeGraphApply (cmd/bd/graph_apply.go).
for _, node := range plan.Nodes {
for _, nd := range node.Deps {
dep, err := types.NewGraphNodeDependency(keyToID[node.Key], nd.Type, nd.Target, keyToID)
if err != nil {
return GraphApplyResult{}, fmt.Errorf("applyGraph: node %q: %w", node.Key, err)
}
if (dep.Type == types.DepParentChild) != parentPhase {View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped cause (%w) for the constructor's specific complaint and fix that edge field
- Validate gate syntax client-side before building the plan
- Ensure SpawnerKey/SpawnerID resolve (add a plan node or clear the field)
- Upgrade/downgrade the client so edge fields match the installed beads version
Example fix
// before
GraphEdge{FromKey: "bd-1", ToKey: "bd-2", Type: "conditional-blocks", Gate: "status == open &&"} // malformed gate
// after
GraphEdge{FromKey: "bd-1", ToKey: "bd-2", Type: "conditional-blocks", Gate: "status == open"} Defensive patterns
Strategy: try-catch
Validate before calling
// Validate edge extras before building the plan:
for i, e := range plan.Edges {
if e.Gate != "" && !validGateSyntax(e.Gate) {
return fmt.Errorf("edge %d: malformed gate %q", i, e.Gate)
}
if e.SpawnerKey != "" {
if _, ok := keyToID[e.SpawnerKey]; !ok { return fmt.Errorf("edge %d: spawner %q unresolved", i, e.SpawnerKey) }
}
} Type guard
func edgeConstructible(e GraphEdge, keyToID map[string]string) bool {
if e.Gate != "" && !validGateSyntax(e.Gate) { return false }
if e.SpawnerKey != "" {
if _, ok := keyToID[e.SpawnerKey]; !ok { return false }
}
return true
} Try / catch
if err := applyGraphEdges(ctx, plan); err != nil {
if strings.HasPrefix(err.Error(), "applyGraph: edge ") && !strings.Contains(err.Error(), "->") {
// constructor-stage failure: fix the named edge's gate/spawner/thread fields
idx := parseEdgeIndex(err.Error())
log.Printf("edge %d rejected by NewGraphEdgeDependency: %v", idx, err)
}
return err
} Prevention
- Validate gate expressions and spawner references before plan submission
- Keep client and beads versions aligned to avoid field-shape mismatches
- Only reference threads/spawners from issues included in or present in the DB
- Unit-test plan builders against NewGraphEdgeDependency's constraints
When it happens
Trigger: NewGraphEdgeDependency returns an error for edge i: invalid gate spec, spawner key/id unresolvable, unsupported type/gate combination, or malformed thread reference in the plan edge.
Common situations: Hand-authored gate expressions with syntax errors; spawner key pointing at an issue not in the plan; a beads version mismatch where the client emits edge fields the constructor rejects; thread ID copied from another issue.
Related errors
- edge %s->%s: %w
- failed to check parent issue: %w
- load create context: %w
- graph create: %w
- getting open issues: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/38a4346520f829a3.
Report an issue: GitHub.