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 the

View on GitHub (pinned to 71377f2769)

Solutions

  1. Set request.Actor to the operator/session identity before calling the batch close API.
  2. Match with errors.Is(err, storage.ErrValidation) and surface a clear 'actor required' message to the user.
  3. In server/CLI layers, require an --actor flag or authenticated identity and fail fast before building the request.
  4. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/6a2b28210ce8a5f7. Report an issue: GitHub.