{"record":{"id":"b714dd44ddcb577c","repo":"gastownhall/beads","slug":"w-apply-batch-item-d-s-must-name-a-key-or-an","errorCode":null,"errorMessage":"%w: apply batch item %d: %s must name a key or an id","messagePattern":"%w: apply batch item (.+?): (.+?) must name a key or an id","errorType":"validation","errorClass":"issueops.ErrValidation","httpStatus":null,"severity":"error","filePath":"internal/storage/batch_apply.go","lineNumber":324,"sourceCode":"\t}\n\tif ref.Key == \"\" {\n\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.","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/batch_apply.go#L306-L342","documentation":"This error is thrown by validateApplyRef when an item in an apply batch references an issue (as a dependency endpoint or target) without naming it: both ref.Key and ref.ID are empty. The batch applier needs exactly one identifier to resolve the ref, so it rejects the item at plan time with a wrapped issueops.ErrValidation.","triggerScenarios":"Calling a batch apply API (planApplyBatchCreate path, or a dep/target ref validated via validateApplyTargetRef) with a Ref struct where both Key and ID are the empty string.","commonSituations":"Constructing batch items programmatically from structs where the ref field was never populated; deserializing JSON payloads that omit the key/id fields; copying a Ref after clearing fields; passing a zero-value Ref.","solutions":["Populate exactly one of ref.Key or ref.ID on every batch item before calling the apply API","Validate all refs client-side before building the batch","Check the payload/JSON actually carries the key or id field for each item"],"exampleFix":"// before\nrefs := []issueops.Ref{{}}\n// after\nrefs := []issueops.Ref{{Key: \"beads-42\"}}","handlingStrategy":"validation","validationCode":"func validateRef(r issueops.Ref, i int, member string) error {\n\tif r.Key == \"\" && r.ID == \"\" {\n\t\treturn fmt.Errorf(\"item %d: %s needs a key or id\", i, member)\n\t}\n\treturn nil\n}","typeGuard":"func refNamed(r issueops.Ref) bool { return r.Key != \"\" || r.ID != \"\" }","tryCatchPattern":null,"preventionTips":["Always populate Key or ID when building batch items","Validate the whole batch before submitting","Unmarshal into Ref structs with required-field checks"],"tags":["validation","batch-apply","storage"],"backgroundTag":"ref-missing-identifier","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}