{"record":{"id":"408ea2d70ed9d8ce","repo":"gastownhall/beads","slug":"duplicate-node-key-q","errorCode":null,"errorMessage":"duplicate node key %q","messagePattern":"duplicate node key %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/graph_apply.go","lineNumber":536,"sourceCode":"\t\t\treturn fmt.Errorf(\"explicit id %q (node %q) already exists; graph create will not overwrite an existing issue — drop the id to mint a new one, or update the existing issue instead\", node.ID, node.Key)\n\t\t}\n\t}\n\treturn nil\n}\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","sourceCodeStart":518,"sourceCodeEnd":554,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/graph_apply.go#L518-L554","documentation":"During `bd graph apply` plan validation, each node's key is checked against a set of already-seen keys. If two nodes in plan.nodes share the same key, the apply is rejected with this error. Keys must be unique because they are the identity used for parent/dep/metadata references.","triggerScenarios":"Calling `bd graph apply` with a plan whose nodes array contains two entries with identical `key` values.","commonSituations":"Hand-written or template-generated plan files where a node was copy-pasted without renaming its key; script loops that append nodes with a constant key; merging plans from different sources that happen to use the same keys.","solutions":["Find the duplicated key in the plan file and give one of the nodes a unique key","Update all parent_key, deps[].target, metadata_refs, and edge from/to references that pointed at the renamed key","Regenerate the plan programmatically ensuring key uniqueness before applying"],"exampleFix":"// before\n\"nodes\": [{\"key\": \"auth\"}, {\"key\": \"auth\"}]\n// after\n\"nodes\": [{\"key\": \"auth-login\"}, {\"key\": \"auth-logout\"}]","handlingStrategy":"validation","validationCode":"keys := map[string]bool{}\nfor _, n := range plan.Nodes {\n\tif keys[n.Key] { return fmt.Errorf(\"duplicate node key %q\", n.Key) }\n\tkeys[n.Key] = true\n}","typeGuard":null,"tryCatchPattern":"if err := bd.GraphApply(ctx, plan, opts); err != nil {\n\tif strings.HasPrefix(err.Error(), \"duplicate node key\") {\n\t\t// fix plan and retry\n\t}\n}","preventionTips":["Derive node keys from a unique source (e.g. slugified titles plus counter)","Run a pre-apply uniqueness check on the plan JSON","Avoid copy-pasting node blocks without renaming keys"],"tags":["validation","graph-apply","duplicate-key"],"backgroundTag":"duplicate-key-in-plan","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}