gastownhall/beads · warning
%w: close batch requires at least one item
Error message
%w: close batch requires at least one item
What it means
ValidateCloseBatchRequest rejects a CloseBatchRequest with zero items, wrapping storage.ErrValidation. A batch close with no items is a no-op misuse rather than something a backend should silently succeed on, so the shared contract rejects it up front. Callers should match with errors.Is(err, storage.ErrValidation).
Source
Thrown at internal/storage/issueops/close_batch.go:23
"database/sql"
"fmt"
"strings"
"github.com/steveyegge/beads/internal/storage"
"github.com/steveyegge/beads/internal/types"
publicops "github.com/steveyegge/beads/issueops"
)
// ValidateCloseBatchRequest applies the request rules every BatchCloser
// implementation shares, so a rule is a contract rather than one backend's
// habit. It rejects the request outright; a per-item refusal is a result, not
// a validation failure, and never reaches here.
func ValidateCloseBatchRequest(request publicops.CloseBatchRequest) error {
if request.Actor == "" {
return fmt.Errorf("%w: close batch requires an actor", storage.ErrValidation)
}
if len(request.Items) == 0 {
return fmt.Errorf("%w: close batch requires at least one item", storage.ErrValidation)
}
for i, item := range request.Items {
if item.IssueID == "" {
return fmt.Errorf("%w: close batch item %d requires an issue ID", storage.ErrValidation, i)
}
}
if request.ClaimNext != nil {
if err := ValidateClaimNextRequest(publicops.ClaimNextRequest{Actor: request.Actor, Filter: *request.ClaimNext}); err != nil {
return err
}
}
return nil
}
// CloseBatchCommitMessage is the history entry a batch records. It is the
// only spelling — the request carries no label to override it, and could not
// compose this one, because it names what LANDED rather than what was asked for,
// which is why it is composed from the result and not from the request: aView on GitHub (pinned to 71377f2769)
Solutions
- Guard before calling: check len(request.Items) > 0 and skip or report 'nothing to close' instead of issuing the call.
- Match errors.Is(err, storage.ErrValidation) and return a friendly 'no issues selected' message.
- Fix upstream selection logic that produced an empty candidate list unexpectedly.
- In API layers, return 400-style validation output before touching storage.
Example fix
// before
err := closer.CloseBatch(ctx, publicops.CloseBatchRequest{Actor: actor, Items: nil})
// after
if len(items) == 0 {
return fmt.Errorf("no issues to close")
}
err := closer.CloseBatch(ctx, publicops.CloseBatchRequest{Actor: actor, Items: items}) Defensive patterns
Strategy: validation
Validate before calling
if len(items) == 0 {
return fmt.Errorf("no issues selected for batch close")
} Type guard
func hasBatchItems(req publicops.CloseBatchRequest) bool {
return len(req.Items) > 0
} Try / catch
if err := ValidateCloseBatchRequest(req); err != nil {
if errors.Is(err, storage.ErrValidation) && strings.Contains(err.Error(), "at least one item") {
return ErrNothingToClose // typed sentinel for callers
}
return err
} Prevention
- Short-circuit empty candidate lists before issuing a batch call.
- Log when upstream filtering reduces the item set to zero — usually a filter bug.
- Provide an explicit 'nothing to do' UX path instead of an error surprise.
- Cover the empty-input case in tests.
When it happens
Trigger: Passing CloseBatchRequest with a nil or empty Items slice to any BatchCloser implementation that invokes ValidateCloseBatchRequest.
Common situations: Upstream filtering (e.g. filtering a list of issues by status) accidentally removed every candidate before batching; loops that append to the wrong slice; JSON payloads where items was omitted or empty; tests with unpopulated fixtures.
Related errors
- %w: close batch requires an actor
- %w: close batch item %d requires an issue ID
- proxy.ForceStopUnverified: at most one options value is allo
- %s cannot be empty
- no rules to compact
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/7e88db3b5bf9fa62.
Report an issue: GitHub.