{"record":{"id":"df01b9c45ade794c","repo":"gastownhall/beads","slug":"w-apply-batch-item-d-has-a-metadata-ref-with-an","errorCode":null,"errorMessage":"%w: apply batch item %d has a metadata_ref with an empty key","messagePattern":"%w: apply batch item (.+?) has a metadata_ref with an empty key","errorType":"validation","errorClass":"issueops.ErrValidation","httpStatus":null,"severity":"error","filePath":"internal/storage/batch_apply.go","lineNumber":201,"sourceCode":"\t\t\treturn err\n\t\t}\n\t\titem.DepAdd = &edge\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"%w: apply batch item %d has unknown kind %q\", issueops.ErrValidation, index, item.Kind)\n}\n\n// planApplyBatchCreate checks a create's metadata refs and records the row it\n// mints as touched.\n//\n// A METADATA REF MAY NAME ANY KEY, including this item's own and one declared\n// later. Every id is minted before any splice runs, so the direction cannot\n// matter — see issueops.CreateItem.MetadataRefs. What it may not do is name a\n// key no item declares.\nfunc planApplyBatchCreate(item *issueops.CreateItem, index int, keyIndex map[string]int, touched map[string]bool) error {\n\tfor metaKey, ref := range item.MetadataRefs {\n\t\tif metaKey == \"\" {\n\t\t\treturn fmt.Errorf(\"%w: apply batch item %d has a metadata_ref with an empty key\", issueops.ErrValidation, index)\n\t\t}\n\t\tif err := validateApplyRef(ref, index, \"metadata_ref \"+metaKey); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif ref.Key == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := keyIndex[ref.Key]; !ok {\n\t\t\treturn &issueops.RefError{Index: index, Member: \"metadata_ref \" + metaKey, Key: ref.Key}\n\t\t}\n\t}\n\tif item.Key != \"\" {\n\t\ttouched[applyTouchKeyRef(issueops.Ref{Key: item.Key})] = true\n\t}\n\tif item.Issue != nil && item.Issue.ID != \"\" {\n\t\ttouched[applyTouchKeyRef(issueops.Ref{ID: item.Issue.ID})] = true\n\t}\n\treturn nil","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/batch_apply.go#L183-L219","documentation":"Each entry in CreateItem.MetadataRefs is keyed by a metadata key; an empty-string map key is meaningless and rejected. This error fires when a create item's MetadataRefs map contains an entry whose key is \"\". Wraps issueops.ErrValidation.","triggerScenarios":"PlanApplyBatch with a create item built like MetadataRefs: map[string]issueops.Ref{\"\": {Key: \"other\"}} — e.g. dynamically populating the map from data where the metadata key was missing.","commonSituations":"Populating MetadataRefs by iterating over a source map that has empty keys; slicing/config where the metadata name column was blank; a nil/zero metadata name before insertion.","solutions":["Filter out entries with empty metadata keys before building MetadataRefs","Fix the upstream source so every metadata entry has a non-empty name","Skip (or error at ingest) records missing a metadata key rather than adding them with \"\""],"exampleFix":"// before\nrefs[k.Name] = issueops.Ref{Key: k.RefKey} // k.Name may be \"\"\n// after\nif k.Name == \"\" { return fmt.Errorf(\"metadata entry missing name\") }\nrefs[k.Name] = issueops.Ref{Key: k.RefKey}","handlingStrategy":"validation","validationCode":"for i, it := range items {\n  if it.Create == nil { continue }\n  for k := range it.Create.MetadataRefs {\n    if k == \"\" { return fmt.Errorf(\"create item %d has empty metadata key\", i) }\n  }\n}","typeGuard":"func hasEmptyMetaKey(it *issueops.CreateItem) bool {\n  if it == nil { return false }\n  _, bad := it.MetadataRefs[\"\"]\n  return bad\n}","tryCatchPattern":"if err := store.PlanApplyBatch(plan); err != nil {\n  if errors.Is(err, issueops.ErrValidation) && strings.Contains(err.Error(), \"empty key\") {\n    // sanitize MetadataRefs keys and rebuild the plan\n  }\n  return err\n}","preventionTips":["Sanitize upstream metadata maps: drop or reject entries with empty keys","Validate metadata names at ingest time","Prefer explicit error at data-load over silently adding \"\" keys"],"tags":["validation","batch-apply","metadata","empty-key"],"backgroundTag":"empty-metadata-key","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}