gastownhall/beads · error · issueops.ErrValidation
%w: apply batch item %d has a metadata_ref with an empty key
Error message
%w: apply batch item %d has a metadata_ref with an empty key
What it means
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.
Source
Thrown at internal/storage/batch_apply.go:201
return err
}
item.DepAdd = &edge
return nil
}
return fmt.Errorf("%w: apply batch item %d has unknown kind %q", issueops.ErrValidation, index, item.Kind)
}
// planApplyBatchCreate checks a create's metadata refs and records the row it
// mints as touched.
//
// A METADATA REF MAY NAME ANY KEY, including this item's own and one declared
// later. Every id is minted before any splice runs, so the direction cannot
// matter — see issueops.CreateItem.MetadataRefs. What it may not do is name a
// key no item declares.
func planApplyBatchCreate(item *issueops.CreateItem, index int, keyIndex map[string]int, touched map[string]bool) error {
for metaKey, ref := range item.MetadataRefs {
if metaKey == "" {
return fmt.Errorf("%w: apply batch item %d has a metadata_ref with an empty key", issueops.ErrValidation, index)
}
if err := validateApplyRef(ref, index, "metadata_ref "+metaKey); err != nil {
return err
}
if ref.Key == "" {
continue
}
if _, ok := keyIndex[ref.Key]; !ok {
return &issueops.RefError{Index: index, Member: "metadata_ref " + metaKey, Key: ref.Key}
}
}
if item.Key != "" {
touched[applyTouchKeyRef(issueops.Ref{Key: item.Key})] = true
}
if item.Issue != nil && item.Issue.ID != "" {
touched[applyTouchKeyRef(issueops.Ref{ID: item.Issue.ID})] = true
}
return nilView on GitHub (pinned to 71377f2769)
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 ""
Example fix
// before
refs[k.Name] = issueops.Ref{Key: k.RefKey} // k.Name may be ""
// after
if k.Name == "" { return fmt.Errorf("metadata entry missing name") }
refs[k.Name] = issueops.Ref{Key: k.RefKey} Defensive patterns
Strategy: validation
Validate before calling
for i, it := range items {
if it.Create == nil { continue }
for k := range it.Create.MetadataRefs {
if k == "" { return fmt.Errorf("create item %d has empty metadata key", i) }
}
} Type guard
func hasEmptyMetaKey(it *issueops.CreateItem) bool {
if it == nil { return false }
_, bad := it.MetadataRefs[""]
return bad
} Try / catch
if err := store.PlanApplyBatch(plan); err != nil {
if errors.Is(err, issueops.ErrValidation) && strings.Contains(err.Error(), "empty key") {
// sanitize MetadataRefs keys and rebuild the plan
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- %w: apply batch item %d: %v
- ExternalDoltConfig: must set Socket or (Host, Port)
- metadata.repo: %w
- metadata.repo must not be null
- invalid repo metadata: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/df01b9c45ade794c.
Report an issue: GitHub.