{"record":{"id":"191463d3ebf784bd","repo":"gastownhall/beads","slug":"node-q-has-empty-title","errorCode":null,"errorMessage":"node %q has empty title","messagePattern":"node %q has empty title","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/graph_apply.go","lineNumber":540,"sourceCode":"}\n\nfunc validateGraphApplyPlan(plan *GraphApplyPlan, customTypes, customStatuses []string, opts GraphApplyOptions) error {\n\tif len(plan.Nodes) == 0 {\n\t\treturn fmt.Errorf(\"plan has no nodes\")\n\t}\n\n\tseenKeys := make(map[string]bool, len(plan.Nodes))\n\tseenIDs := make(map[string]bool)\n\tfor i, node := range plan.Nodes {\n\t\tif node.Key == \"\" {\n\t\t\treturn fmt.Errorf(\"node %d has empty key\", i)\n\t\t}\n\t\tif seenKeys[node.Key] {\n\t\t\treturn fmt.Errorf(\"duplicate node key %q\", node.Key)\n\t\t}\n\t\tseenKeys[node.Key] = true\n\t\tif node.Title == \"\" {\n\t\t\treturn fmt.Errorf(\"node %q has empty title\", node.Key)\n\t\t}\n\t\tif err := validateGraphApplyNodeFields(node, customTypes, customStatuses, opts); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif node.ID != \"\" {\n\t\t\tif seenIDs[node.ID] {\n\t\t\t\treturn fmt.Errorf(\"duplicate explicit id %q (node %q)\", node.ID, node.Key)\n\t\t\t}\n\t\t\tseenIDs[node.ID] = true\n\t\t}\n\t\t// Validate MetadataRefs point to known keys.\n\t\tfor metaKey, refKey := range node.MetadataRefs {\n\t\t\tif !seenKeys[refKey] {\n\t\t\t\tfound := false\n\t\t\t\tfor _, other := range plan.Nodes {\n\t\t\t\t\tif other.Key == refKey {\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t\tbreak","sourceCodeStart":522,"sourceCodeEnd":558,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/graph_apply.go#L522-L558","documentation":"Plan validation requires every node in plan.nodes to have a non-empty `title`. A node with an empty title is rejected before any issue is created, since titles are required fields for beads issues.","triggerScenarios":"`bd graph apply` with a plan where a node object has `\"title\": \"\"` or omits the title field.","commonSituations":"Generated plans where the title field was never populated; hand-edited plan files where a title was accidentally cleared; template placeholders left unfilled.","solutions":["Set a descriptive title on the node flagged in the error","If the node is unwanted, remove it from plan.nodes entirely","Fix the generating script/template so titles are always filled"],"exampleFix":"// before\n{\"key\": \"auth-login\", \"title\": \"\"}\n// after\n{\"key\": \"auth-login\", \"title\": \"Implement login endpoint\"}","handlingStrategy":"validation","validationCode":"for i, n := range plan.Nodes {\n\tif strings.TrimSpace(n.Title) == \"\" { return fmt.Errorf(\"node %d (%s) missing title\", i, n.Key) }\n}","typeGuard":null,"tryCatchPattern":"if err := bd.GraphApply(ctx, plan, opts); err != nil {\n\tif strings.Contains(err.Error(), \"has empty title\") {\n\t\t// fill title in plan and retry\n\t}\n}","preventionTips":["Always set titles when constructing nodes","Validate plan files with a JSON schema requiring title","Add template placeholders that fail loudly when unfilled"],"tags":["validation","graph-apply","missing-field"],"backgroundTag":"missing-required-field","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}