gastownhall/beads · warning
%w: close batch requires an actor
Error message
%w: close batch requires an actor
What it means
ValidateCloseBatchRequest rejects a CloseBatchRequest whose Actor field is empty, wrapping storage.ErrValidation so callers can match with errors.Is. Batch close operations must attribute every close to an actor for the audit event, so an unattributed request is a programming/API misuse rejected before any storage work. Per-item refusals are results, not this validation error.
Source
Thrown at internal/storage/issueops/close_batch.go:20
import (
"context"
"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 theView on GitHub (pinned to 71377f2769)
Solutions
- Set request.Actor to the operator/session identity before calling the batch close API.
- Match with errors.Is(err, storage.ErrValidation) and surface a clear 'actor required' message to the user.
- In server/CLI layers, require an --actor flag or authenticated identity and fail fast before building the request.
- Add a constructor/helper for CloseBatchRequest that makes Actor a required argument.
Example fix
// before
req := publicops.CloseBatchRequest{Items: items}
err := closer.CloseBatch(ctx, req)
// after
req := publicops.CloseBatchRequest{Actor: actor, Items: items}
if req.Actor == "" {
return fmt.Errorf("close batch: actor is required")
}
err := closer.CloseBatch(ctx, req) Defensive patterns
Strategy: validation
Validate before calling
func validateCloseReq(req publicops.CloseBatchRequest) error {
if req.Actor == "" {
return fmt.Errorf("close batch requires an actor")
}
return nil
} Try / catch
if err := ValidateCloseBatchRequest(req); err != nil {
if errors.Is(err, storage.ErrValidation) {
return fmt.Errorf("invalid close batch request: %w", err)
}
return err
} Prevention
- Make Actor a required constructor argument for CloseBatchRequest.
- Thread the authenticated identity through every layer instead of defaulting to "".
- Validate the request at the CLI/HTTP boundary before reaching storage.
- Add tests asserting requests without Actor are rejected.
When it happens
Trigger: Constructing publicops.CloseBatchRequest without setting Actor (or setting it to "") and passing it to a BatchCloser implementation that runs shared validation first.
Common situations: Copy-pasted request structs missing the Actor field; refactored callers that dropped actor propagation; tests building requests with only Items populated; deserialized JSON lacking the actor key.
Related errors
- %w: close batch requires at least one item
- %w: close batch item %d requires an issue ID
- proxy.ForceStopUnverified: at most one options value is allo
- nil entry
- kind is required
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/6a2b28210ce8a5f7.
Report an issue: GitHub.