gastownhall/beads · error
applyGraph: edge %d references undefined from_key %q
Error message
applyGraph: edge %d references undefined from_key %q
What it means
applyGraph() validates every edge in an incoming graph plan before writing anything: it resolves each edge's FromKey (or FromID) against the set of issue IDs known to this transaction (keyToID). If the from-side key does not resolve to an existing or same-plan issue, the whole apply is aborted with this error so no partial graph is persisted.
Source
Thrown at internal/storage/domain/issue.go:1179
// Build the (childID, parentID) pair set and validate that any planned
// parent-child link does not close a cycle through planned edges or
// already-existing dependencies in the store. This must run before any
// dep inserts to catch the violation before we've written anything.
parentDepPairs := graphParentDepPairs(plan.Nodes, keyToID)
newSchedulingEdges := make([][2]string, 0, len(plan.Nodes)+len(plan.Edges))
if err := u.validatePlannedBlockingPaths(ctx, plan, keyToID, parentDepPairs); err != nil {
return GraphApplyResult{}, err
}
if err := u.validatePlannedBlockingCycles(ctx, plan, keyToID); err != nil {
return GraphApplyResult{}, err
}
// Preserve failure-before-write for explicit edges that conflict directly
// with an implicit node parent relationship. Parent-first mutation below is
// for transitive hierarchy visibility, not for deferring structural errors.
for i, edge := range plan.Edges {
fromID := resolveEdgeRef(edge.FromKey, edge.FromID, keyToID)
if fromID == "" {
return GraphApplyResult{}, fmt.Errorf("applyGraph: edge %d references undefined from_key %q", i, edge.FromKey)
}
toID := resolveEdgeRef(edge.ToKey, edge.ToID, keyToID)
if toID == "" {
return GraphApplyResult{}, fmt.Errorf("applyGraph: edge %d references undefined to_key %q", i, edge.ToKey)
}
depType := edge.Type
if depType == "" {
depType = types.DepBlocks
}
if parentDepPairs[depPairKey(fromID, toID)] && depType != types.DepParentChild {
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)
}
}
// Pass 3 — insert node parent-child deps now that all IDs are known. TheseView on GitHub (pinned to 71377f2769)
Solutions
- Ensure the from-issue is created in the same plan (add a node) or already exists via bd list/show before applying
- Verify the issue key spelling (bd show <key> must succeed first)
- Set edge.FromID explicitly if you have the UUID and the key is unreliable
- Sync/pull so remote issues referenced by the plan exist locally
Example fix
// before
plan.Edges = append(plan.Edges, GraphEdge{FromKey: "bd-999", ToKey: "bd-1"}) // bd-999 doesn't exist
// after
if _, err := s.GetIssue(ctx, "bd-999"); err != nil {
plan.Nodes = append(plan.Nodes, GraphNode{Key: "bd-999"}) // include it in the plan
}
plan.Edges = append(plan.Edges, GraphEdge{FromKey: "bd-999", ToKey: "bd-1"}) Defensive patterns
Strategy: validation
Validate before calling
for _, e := range plan.Edges {
if _, ok := keyToID[e.FromKey]; !ok && e.FromID == "" {
return fmt.Errorf("edge from_key %q has no node and no FromID", e.FromKey)
}
} Type guard
func edgeFromResolved(e GraphEdge, keyToID map[string]string) bool {
_, byKey := keyToID[e.FromKey]
return byKey || e.FromID != ""
} Prevention
- Always include a node for every edge endpoint in the plan
- Run bd show <key> before referencing an issue
- Prefer FromID when keys may be stale
- Sync before applying plans produced on another machine
When it happens
Trigger: Calling applyGraph with a plan whose Edges[i].FromKey names an issue that is not in the database and not created by an earlier node in the same plan, and where no FromID fallback is set.
Common situations: Client built the plan against a different database/worktree; issue key typo'd or stale after deletion; edge references an issue from another repo that was never synced; plan JSON was hand-edited.
Related errors
- applyGraph: edge %d references undefined to_key %q
- no store is open for this workspace
- not found
- db: ChildCounterSQLRepository.NextChildID: parentID must not
- db: DependencySQLRepository.Insert: dep must not be nil
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c8de349c325022c8.
Report an issue: GitHub.