gastownhall/beads · error · issueops.ErrValidation
%w: apply batch item %d reuses key %q, already declared by i
Error message
%w: apply batch item %d reuses key %q, already declared by item %d
What it means
Each create item may declare a Key so later items can reference it; keys must be unique within the batch. This error fires when two create items declare the same Key, since the key-to-item map would be ambiguous. Wraps issueops.ErrValidation.
Source
Thrown at internal/storage/batch_apply.go:157
if !matches {
return nil, fmt.Errorf("%w: apply batch item %d is kind %q but carries another kind's payload",
issueops.ErrValidation, i, item.Kind)
}
if item.Create == nil {
continue
}
if item.Create.Issue == nil {
return nil, fmt.Errorf("%w: apply batch item %d requires an issue", issueops.ErrValidation, i)
}
if len(item.Create.Issue.Comments) > 0 || len(item.Create.Issue.Dependencies) > 0 {
return nil, fmt.Errorf("%w: apply batch item %d must not carry comments or dependencies on the issue; edges are their own items",
issueops.ErrValidation, i)
}
if item.Create.Key == "" {
continue
}
if prior, dup := keyIndex[item.Create.Key]; dup {
return nil, fmt.Errorf("%w: apply batch item %d reuses key %q, already declared by item %d",
issueops.ErrValidation, i, item.Create.Key, prior)
}
keyIndex[item.Create.Key] = i
}
return keyIndex, nil
}
// planApplyBatchItem validates one item's refs, guards and edge metadata, and
// records what it touches for the items after it.
func planApplyBatchItem(item *issueops.ApplyItem, index int, keyIndex map[string]int, touched map[string]bool) error {
switch item.Kind {
case issueops.ItemCreate:
return planApplyBatchCreate(item.Create, index, keyIndex, touched)
case issueops.ItemUpdate:
return planApplyBatchUpdate(item.Update, index, keyIndex, touched)
case issueops.ItemClose:
return planApplyBatchClose(item.Close, index, keyIndex, touched)
case issueops.ItemDepAdd:View on GitHub (pinned to 71377f2769)
Solutions
- Make each Create.Key unique (append a suffix/counter)
- Remove the Key from the duplicate item if nothing references it (empty keys are allowed)
- Track used keys in the generator and skip or rename duplicates
Example fix
// before
for _, it := range issues { items = append(items, mkCreate("issue", it)) } // key always "issue"
// after
for i, it := range issues { items = append(items, mkCreate(fmt.Sprintf("issue-%d", i), it)) } Defensive patterns
Strategy: validation
Validate before calling
seen := map[string]bool{}
for i, it := range items {
if it.Create != nil && it.Create.Key != "" {
if seen[it.Create.Key] {
return fmt.Errorf("item %d reuses key %q", i, it.Create.Key)
}
seen[it.Create.Key] = true
}
} Try / catch
if err := store.PlanApplyBatch(plan); err != nil {
if errors.Is(err, issueops.ErrValidation) && strings.Contains(err.Error(), "reuses key") {
// regenerate unique keys and rebuild the plan
}
return err
} Prevention
- Template keys with an index or slug to guarantee uniqueness
- Leave Key empty for items nothing references
- Centralize key generation in one function that de-duplicates
When it happens
Trigger: PlanApplyBatch with two create items both setting Create.Key to the same string (e.g. two items with Key "bd-1"); the second one triggers the error.
Common situations: Reusing a slug/key across loop iterations without templating it; appending a fixture batch twice; generating keys from a field that isn't unique in the source data.
Related errors
- duplicate node key %q
- %w: apply batch item %d is kind %q but carries another kind'
- %w: apply batch item %d requires an issue
- %w: apply batch item %d must not carry comments or dependenc
- %w: apply batch item %d has a metadata_ref with an empty key
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/64f3fac61cf84c14.
Report an issue: GitHub.