{"record":{"id":"4d13a54698193eab","repo":"gastownhall/beads","slug":"w-apply-batch-item-d-s-names-both-key-q-and","errorCode":null,"errorMessage":"%w: apply batch item %d: %s names both key %q and id %q; exactly one","messagePattern":"%w: apply batch item (.+?): (.+?) names both key %q and id %q; exactly one","errorType":"validation","errorClass":"issueops.ErrValidation","httpStatus":null,"severity":"error","filePath":"internal/storage/batch_apply.go","lineNumber":326,"sourceCode":"\t\treturn nil\n\t}\n\tdeclaredAt, ok := keyIndex[ref.Key]\n\tif !ok {\n\t\treturn &issueops.RefError{Index: index, Member: member, Key: ref.Key}\n\t}\n\tif declaredAt >= index {\n\t\treturn &issueops.RefError{Index: index, Member: member, Key: ref.Key, DeclaredLater: true}\n\t}\n\treturn nil\n}\n\n// validateApplyRef checks the exactly-one rule every ref answers to.\nfunc validateApplyRef(ref issueops.Ref, index int, member string) error {\n\tswitch {\n\tcase ref.Key == \"\" && ref.ID == \"\":\n\t\treturn fmt.Errorf(\"%w: apply batch item %d: %s must name a key or an id\", issueops.ErrValidation, index, member)\n\tcase ref.Key != \"\" && ref.ID != \"\":\n\t\treturn fmt.Errorf(\"%w: apply batch item %d: %s names both key %q and id %q; exactly one\",\n\t\t\tissueops.ErrValidation, index, member, ref.Key, ref.ID)\n\t}\n\treturn nil\n}\n\n// applyTouchKeyRef renders a ref as the address the touched set is keyed by.\n// The two namespaces are kept apart so a key and an id that happen to read the\n// same string are not confused for one row.\nfunc applyTouchKeyRef(ref issueops.Ref) string {\n\tif ref.Key != \"\" {\n\t\treturn \"key:\" + ref.Key\n\t}\n\treturn \"id:\" + ref.ID\n}\n\n// applyRefLabel renders a ref for a message.\nfunc applyRefLabel(ref issueops.Ref) string {\n\tif ref.Key != \"\" {","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/batch_apply.go#L308-L344","documentation":"validateApplyRef enforces that each batch ref names an issue by exactly one identifier. This error fires when both ref.Key and ref.ID are set, which would be ambiguous — the applier cannot know which identifier is authoritative.","triggerScenarios":"Passing a Ref with both Key and ID non-empty to a batch apply item (create path or target ref validation).","commonSituations":"Copying a fully-populated issue struct into a Ref instead of picking one field; merging payloads where one source supplied key and another supplied id; ORM lookups that fill both fields.","solutions":["Clear one of the two fields so exactly one identifier remains (prefer Key for readability, ID for stability)","Add client-side normalization that strips ID when Key is present (or vice versa)","Fix the code that populates the Ref to set only one field"],"exampleFix":"// before\nref := issueops.Ref{Key: \"beads-42\", ID: \"bd-a1b2c3\"}\n// after\nref := issueops.Ref{Key: \"beads-42\"}","handlingStrategy":"validation","validationCode":"func exactlyOne(r issueops.Ref) error {\n\tif r.Key != \"\" && r.ID != \"\" {\n\t\treturn fmt.Errorf(\"ref sets both key %q and id %q\", r.Key, r.ID)\n\t}\n\treturn nil\n}","typeGuard":"func refUnambiguous(r issueops.Ref) bool { return (r.Key == \"\") != (r.ID == \"\") }","tryCatchPattern":null,"preventionTips":["Normalize refs at construction: prefer Key, clear ID","Never copy full issue structs into Ref fields","Add a unit test for ref construction helpers"],"tags":["validation","batch-apply","ambiguous-ref"],"backgroundTag":"ref-ambiguous-identifier","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}