gastownhall/beads · error

node %q: setting assignee: %w

Error message

node %q: setting assignee: %w

What it means

After dependencies are applied, graph apply sets each node's deferred assignee via tx.UpdateIssue. This error wraps a failure of that update — the dependencies were written but the assignee field could not be persisted — and the whole transaction (including the deps) is rolled back.

Source

Thrown at cmd/bd/graph_apply.go:1097

					if graphApplySchedulingDependencyType(d.Type) {
						newSchedulingEdges = append(newSchedulingEdges, [2]string{d.IssueID, d.DependsOnID})
					}
				}
			}
		}
		if cyclePath, err := tx.CycleThroughEdges(ctx, newSchedulingEdges); err != nil {
			return fmt.Errorf("final graph cycle check: %w", err)
		} else if cyclePath != "" {
			return fmt.Errorf("graph dependency cycle would be created: %s", cyclePath)
		}

		// Apply deferred assignees.
		for i, assignee := range pendingAssignees {
			updates := map[string]interface{}{
				"assignee": assignee,
			}
			if err := tx.UpdateIssue(ctx, issues[i].ID, updates, actor); err != nil {
				return fmt.Errorf("node %q: setting assignee: %w", plan.Nodes[i].Key, err)
			}
		}

		return nil
	}); err != nil {
		return nil, err
	}

	return &GraphApplyResult{IDs: keyToID}, nil
}

// validateGraphApplyPlannedBlockingCycles rejects planned blocking edges that
// would close a blocking-dependency cycle, evaluated whole-graph before any
// insert. This early preflight is restricted to blocking edges for precise
// plan errors. Each stored edge still runs issueops.CheckDependencyCycleInTx,
// which enforces the combined blocks + conditional-blocks + parent-child graph.
func validateGraphApplyPlannedBlockingCycles(ctx context.Context, tx storage.Transaction, plan *GraphApplyPlan, keyToID map[string]string) error {
	type plannedEdge struct {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped error for the storage-level cause
  2. Verify the assignee value is a valid identity accepted by the backend
  3. Ensure no concurrent process is modifying the same issues during apply
  4. Retry the apply once storage is healthy — the transaction guarantees atomicity

Example fix

// before
{"assignee":""} // empty value rejected
// after
{"assignee":"alice"}
Defensive patterns

Strategy: try-catch

Validate before calling

for i, a := range pendingAssignees {
  if !validAssignee(a) { return fmt.Errorf("invalid assignee %q for node %q", a, plan.Nodes[i].Key) }
}

Type guard

func assigneesValid(plan Plan) bool {
  return every(plan.Nodes, func(n Node) bool { return n.Assignee == "" || validAssignee(n.Assignee) })
}

Try / catch

if err := bd.GraphApply(ctx, plan); err != nil {
  if strings.Contains(err.Error(), "setting assignee") {
    // transaction rolled back; fix assignee values and retry
    return bd.GraphApply(ctx, sanitizeAssignees(plan))
  }
  return err
}

Prevention

When it happens

Trigger: tx.UpdateIssue(ctx, issues[i].ID, {"assignee": assignee}, actor) errors during the pendingAssignees loop, e.g. storage failure, issue row missing, or an invalid assignee value rejected by the backend.

Common situations: Assignee values violating DB constraints, storage connectivity problems late in a long transaction, or concurrent writers deleting/altering the issue mid-apply.

Related errors


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