gastownhall/beads · error
invalid graph plan: %w
Error message
invalid graph plan: %w
What it means
validateProxiedGraphPlan rejected the graph plan inside the transaction. The wrapped error says what is invalid: mixed storage classes, nonexistent dep targets, ID collisions, or malformed nodes. Nothing is committed — the transaction rolls back.
Source
Thrown at cmd/bd/create_proxied_server.go:345
commitMsg := plan.CommitMessage
if commitMsg == "" {
commitMsg = fmt.Sprintf("bd: graph-apply %d nodes", len(plan.Nodes))
}
res, err := uow.RunTxResult(ctx, uowProvider, func(ctx context.Context, uw uow.UnitOfWork) (map[string]string, string, error) {
cctx, err := uw.ConfigUseCase().LoadCreateContext(ctx)
if err != nil {
return nil, "", fmt.Errorf("load create context: %w", err)
}
// validateProxiedGraphPlan enforces a uniform storage class, so its
// resolved useWisp decides which table the whole plan routes to. The
// collision preflight runs inside this transaction, so it cannot race
// a concurrent create of the same explicit ID.
useWisp, err := validateProxiedGraphPlan(&plan, in, cctx, uowIssueExists(ctx, uw))
if err != nil {
return nil, "", fmt.Errorf("invalid graph plan: %w", err)
}
var result domain.GraphApplyResult
var applyErr error
if useWisp {
result, applyErr = uw.IssueUseCase().ApplyWispGraph(ctx, domainPlan, in.createdBy)
} else {
result, applyErr = uw.IssueUseCase().ApplyIssueGraph(ctx, domainPlan, in.createdBy)
}
if applyErr != nil {
return nil, "", fmt.Errorf("graph create: %w", applyErr)
}
return result.IDs, commitMsg, nil
})
if err != nil {
return HandleError("%v", err)
}View on GitHub (pinned to 71377f2769)
Solutions
- Fix the wrapped validation error in the graph file
- Ensure every dep target exists in the plan or the database
- Use a uniform storage class (all wisps or all issues) per plan
- De-duplicate explicit IDs across plan nodes
Example fix
// before: dep target not in plan or DB
{"nodes":[{"id":"bd-1","deps":[{"target":"bd-404"}]}]}
// after: reference an existing node
{"nodes":[{"id":"bd-1","deps":[{"target":"bd-2"}]},{"id":"bd-2"}]} Defensive patterns
Strategy: validation
Validate before calling
// client-side pre-check before graph-apply
ids := map[string]bool{}
for _, n := range plan.Nodes {
if ids[n.ID] { return fmt.Errorf("duplicate id %s", n.ID) }
ids[n.ID] = true
}
for _, n := range plan.Nodes {
for _, d := range n.Deps {
if !ids[d.Target] && !existsOnServer(d.Target) {
return fmt.Errorf("dep target %s not found", d.Target)
}
}
} Prevention
- Keep one storage class (wisp or issue) per plan
- Validate dep targets against the plan + server before applying
- Validate generated graph files in CI before shipping them
When it happens
Trigger: runCreateProxiedGraph applies a plan whose nodes fail validation: inconsistent useWisp routing, dep on a nonexistent ID, duplicate explicit IDs, or invalid node fields (checked via uowIssueExists preflight).
Common situations: Hand-written or generated graph JSON referencing IDs not in the plan or DB; plan mixing wisps and issues; alias/dep typo in the graph file.
Related errors
- load create context: %w
- graph create: %w
- edge %d: must specify to_key or to_id
- edge %d: invalid dependency type %q
- edge %d: gate/spawner fields require type %q
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/652644237a6462bb.
Report an issue: GitHub.