gastownhall/beads · error

applyGraph: node %q: defer assignee: %w

Error message

applyGraph: node %q: defer assignee: %w

What it means

In the final pass, applyGraph applies deferred assignees via issueRepo.Update; this error wraps a failure updating the assignee field for the node whose key is quoted. The issues and dependencies were already created — only the assignee write failed — so the graph may be partially applied.

Source

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

					newSchedulingEdges = append(newSchedulingEdges, [2]string{dep.IssueID, dep.DependsOnID})
				}
			}
		}
	}
	if cyclePath, err := u.depRepo.CycleThroughEdges(ctx, newSchedulingEdges); err != nil {
		return GraphApplyResult{}, fmt.Errorf("applyGraph: final cycle check: %w", err)
	} else if cyclePath != "" {
		return GraphApplyResult{}, fmt.Errorf("applyGraph: dependency cycle would be created: %s", cyclePath)
	}

	// Pass 5 — apply deferred assignees.
	for i, assignee := range pendingAssignees {
		if assignee == "" {
			continue
		}
		id := keyToID[plan.Nodes[i].Key]
		if err := u.issueRepo.Update(ctx, id, map[string]any{"assignee": assignee}, actor, IssueTableOpts{UseWispsTable: useWisp}); err != nil {
			return GraphApplyResult{}, fmt.Errorf("applyGraph: node %q: defer assignee: %w", plan.Nodes[i].Key, err)
		}
	}

	return GraphApplyResult{IDs: keyToID}, nil
}

// graphParentDepPairs encodes the (childID, parentID) parent-child pairs
// implied by the plan's node ParentKey/ParentID fields. Used by applyGraph
// to dedup explicit edges against implicit parent-child relationships and
// to seed the in-memory adjacency for live cycle detection.
func graphParentDepPairs(nodes []GraphNode, keyToID map[string]string) map[string]bool {
	pairs := make(map[string]bool, len(nodes))
	for _, n := range nodes {
		parentID := n.ParentID
		if n.ParentKey != "" {
			parentID = keyToID[n.ParentKey]
		}
		childID := keyToID[n.Key]

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped error; if it's not-found, the node wasn't created — check earlier passes' errors.
  2. Verify wisp vs regular table consistency for nodes carrying assignees.
  3. Re-run the apply (creation passes should be idempotent) to retry only the assignee update.
  4. Set the assignee manually with bd update <id> --assignee if only this field failed.

Example fix

// before: apply fails only on assignee write
bd graph apply plan.yaml
// after: set assignee directly on the created issue
bd update bd-42 --assignee alice
Defensive patterns

Strategy: validation

Validate before calling

// Validate assignees are set only on nodes that will exist in the right table
for i, a := range plan.Nodes[i].Assignee...; {
    if a != "" && tableMismatch(plan.Nodes[i], useWisp) {
        return fmt.Errorf("node %s: assignee with mismatched table", plan.Nodes[i].Key)
    }
}

Try / catch

if err := bd.GraphApply(ctx, plan); err != nil {
    if strings.Contains(err.Error(), "defer assignee") {
        // nodes/edges likely created; set assignees manually
        for _, n := range plan.Nodes {
            _ = exec("bd", "update", n.Key, "--assignee", n.Assignee)
        }
    }
    return err
}

Prevention

When it happens

Trigger: issueRepo.Update on {"assignee": ...} fails for a node: the issue row was not actually created or is in the wrong table (wisp mismatch), the update violates a constraint, or the storage layer errors out. Also possible if keyToID lookup returned an empty/invalid id for the node key.

Common situations: Assignee strings set on wisp nodes while UseWispsTable points at the regular table (or vice versa); DB write failures late in a long apply; plans where a node key silently failed to map to an ID earlier.

Related errors


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