gastownhall/beads · error
applyGraph: node %q: updating metadata refs: %w
Error message
applyGraph: node %q: updating metadata refs: %w
What it means
Wraps a failure from issueRepo.Update when persisting a node's merged metadata JSON back to the store during applyGraph pass 2. The refs were resolved successfully but the metadata update write failed. This is a storage-layer error: record not found, constraint violation, or driver/connection failure.
Source
Thrown at internal/storage/domain/issue.go:1157
}
keyToID[node.Key] = r.Issue.ID
}
// Pass 2 — resolve MetadataRefs now that every node has a minted ID.
// Merges the resolved IDs into the issue's existing metadata JSON and
// writes the result back via Update. Kept inside applyGraph so the CLI
// cannot bypass this step; the proxied caller used to do it post-call.
for _, node := range plan.Nodes {
if len(node.MetadataRefs) == 0 {
continue
}
metaJSON, err := types.MergeMetadataRefs(node.Issue.Metadata, node.MetadataRefs, keyToID)
if err != nil {
return GraphApplyResult{}, fmt.Errorf("applyGraph: node %q: %w", node.Key, err)
}
updates := map[string]any{"metadata": metaJSON}
if err := u.issueRepo.Update(ctx, keyToID[node.Key], updates, actor, IssueTableOpts{UseWispsTable: useWisp}); err != nil {
return GraphApplyResult{}, fmt.Errorf("applyGraph: node %q: updating metadata refs: %w", node.Key, err)
}
}
// 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.View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped inner error for the driver-level cause
- Verify keyToID[node.Key] resolved to a real persisted issue ID
- Confirm the update uses the same wisp tier (UseWispsTable) as the create pass
- Retry transient failures; verify schema/migrations if persistent
Defensive patterns
Strategy: retry
Validate before calling
// verify nodes exist before pass-2 update happens implicitly; for manual retries:
for _, n := range plan.Nodes {
if _, err := uc.GetIssue(ctx, keyToID[n.Key]); err != nil {
return fmt.Errorf("node %q issue %s not persisted", n.Key, keyToID[n.Key])
}
} Try / catch
result, err := uc.ApplyGraph(ctx, plan, actor)
if err != nil && strings.Contains(err.Error(), "updating metadata refs") {
if isTransientStorageError(err) {
time.Sleep(backoff)
result, err = uc.ApplyGraph(ctx, plan, actor) // must be idempotent
}
} Prevention
- Retry apply only if idempotent (nodes check existence)
- Keep wisp routing consistent between create and update passes
- Watch driver health/logs for transient Dolt failures
- Avoid concurrent writes to the same issues during apply
When it happens
Trigger: Calling ApplyGraph where issueRepo.Update (writing {"metadata": metaJSON}) fails for a node — issue row missing, table tier mismatch, or DB error.
Common situations: Issue deleted between create and update phases; wisp/durable routing mismatch; transient Dolt/storage failure; schema drift on the metadata column.
Related errors
- node %q: updating metadata refs: %w
- graph create: %w
- batch create: %w
- node %q: adding parent-child dep: %w
- adding edge %s->%s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/59e5007bc6a4234b.
Report an issue: GitHub.