gastownhall/beads · error

applyGraph: edge %d (%s -> %s): %w

Error message

applyGraph: edge %d (%s -> %s): %w

What it means

applyGraph applies a dependency-graph plan and wraps any failure from inserting an edge (dependency) between two issues with the edge index and its from/to issue IDs. This error means the depRepo.Insert call for one edge failed — the edge was constructed fine, but persisting it into the dependencies table (or wisps table) returned an error. The underlying wrapped error carries the real cause (constraint violation, missing issue, storage failure).

Source

Thrown at internal/storage/domain/issue.go:1264

				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 {
					continue
				}
				if err := u.depRepo.Insert(ctx, dep, actor, DepInsertOpts{UseWispsTable: useWisp}); err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped %w error to identify the root cause (not-found vs constraint vs storage).
  2. Verify every edge endpoint key exists in the plan's nodes and was created in an earlier phase before edges are applied.
  3. Ensure the plan's wisp/non-wisp flag matches the actual target table for both endpoints (check UseWispsTable handling).
  4. If re-applying, make the plan idempotent or clear already-applied edges first.
  5. Retry after resolving transient Dolt/storage connectivity errors.

Example fix

// before: edge references a key absent from the plan's created nodes
{"edges": [{"fromKey": "bd-99", "toKey": "bd-100", "type": "blocks"}]}
// after: ensure bd-99/bd-100 are defined as nodes in the same plan or already exist
{"nodes": [{"key": "bd-99"}, {"key": "bd-100"}], "edges": [{"fromKey": "bd-99", "toKey": "bd-100", "type": "blocks"}]}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check: every edge endpoint must exist (or be a plan node) before apply
for _, e := range plan.Edges {
    if !keysInPlanOrDB(e.FromKey) || !keysInPlanOrDB(e.ToKey) {
        return fmt.Errorf("edge %s->%s references unknown issue", e.FromKey, e.ToKey)
    }
}

Type guard

func edgeEndpointsResolved(e Edge, keyToID map[string]string) bool {
    return resolveEdgeRef(e.FromKey, e.FromID, keyToID) != "" &&
        resolveEdgeRef(e.ToKey, e.ToID, keyToID) != ""
}

Try / catch

if err := bd.GraphApply(ctx, plan); err != nil {
    var edgeErr *graphEdgeError
    if errors.As(err, &edgeErr) {
        log.Printf("edge %d (%s -> %s) failed: %v", edgeErr.Index, edgeErr.From, edgeErr.To, errors.Unwrap(err))
    }
    return err
}

Prevention

When it happens

Trigger: Calling bd graph apply (or the embedded applyGraph path) with a plan whose edges reference issues where the Insert violates constraints: target issue not found in the same table (issues vs wisps mismatch when useWisp is wrong), a duplicate dependency edge, or an underlying Dolt/storage error during Insert.

Common situations: Applying a plan generated against a different database state (edges pointing at issues that were deleted or not created in an earlier phase); mixed wisp/non-wisp nodes where the UseWispsTable flag doesn't match where both endpoints live; network/DB failures during a large apply; re-applying a plan that already inserted the edge under a unique constraint.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/f5ebe26a751eee41. Report an issue: GitHub.