gastownhall/beads · error · issueops.ErrValidation
%w: apply batch item %d requires an issue
Error message
%w: apply batch item %d requires an issue
What it means
A create-kind batch item must embed an Issue payload; this error fires when item.Create is non-nil but item.Create.Issue is nil. The planner needs the issue row to know what to insert, and there is nothing to plan without it. Wraps issueops.ErrValidation.
Source
Thrown at internal/storage/batch_apply.go:147
matches = item.Create != nil
case issueops.ItemUpdate:
matches = item.Update != nil
case issueops.ItemClose:
matches = item.Close != nil
case issueops.ItemDepAdd:
matches = item.DepAdd != nil
default:
return nil, fmt.Errorf("%w: apply batch item %d has unknown kind %q", issueops.ErrValidation, i, item.Kind)
}
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, andView on GitHub (pinned to 71377f2769)
Solutions
- Assign a complete CreateItem.Issue before calling PlanApplyBatch
- Check the item construction path for a branch that skips setting Issue
- Assert non-nil Issue in the code that builds the batch
Example fix
// before
item := issueops.ApplyItem{Kind: issueops.ItemCreate, Create: &issueops.CreateItem{Key: "bd-1"}}
// after
item := issueops.ApplyItem{Kind: issueops.ItemCreate, Create: &issueops.CreateItem{Key: "bd-1", Issue: &types.Issue{Title: "Fix login", Status: "open"}}} Defensive patterns
Strategy: validation
Validate before calling
for i, it := range items {
if it.Kind == issueops.ItemCreate && it.Create != nil && it.Create.Issue == nil {
return fmt.Errorf("create item %d has no Issue", i)
}
} Type guard
func hasIssue(it *issueops.CreateItem) bool { return it != nil && it.Issue != nil } Try / catch
if err := store.PlanApplyBatch(plan); err != nil {
if errors.Is(err, issueops.ErrValidation) && strings.Contains(err.Error(), "requires an issue") {
// rebuild the plan with the Issue populated
}
return err
} Prevention
- Always construct CreateItem with both Key and Issue in one helper
- Check for skipped assignments when Issue is built conditionally
- Validate payload completeness at the batch-builder boundary
When it happens
Trigger: PlanApplyBatch with an item where Kind is ItemCreate and Create != nil, but Create.Issue was never assigned (zero-value CreateItem or Issue field forgotten).
Common situations: Constructing CreateItem{Key: "x"} and populating the issue later in code that errored or was skipped; unmarshaling partial JSON where the issue object was absent.
Related errors
- %w: apply batch item %d is kind %q but carries another kind'
- %w: apply batch item %d must not carry comments or dependenc
- %w: apply batch item %d reuses key %q, already declared by i
- %w: apply batch item %d has a metadata_ref with an empty key
- %w: apply batch item %d: %s cannot depend on itself
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/2dfe561d3fdd8ddc.
Report an issue: GitHub.