gastownhall/beads · error

%w: add comment requires an issue ID

Error message

%w: add comment requires an issue ID

What it means

ValidateAddCommentRequest rejects an AddCommentRequest with an empty IssueID. A comment must anchor to an existing issue or wisp row; with no ID there is nothing to attach to, so the request fails validation with wrapped storage.ErrValidation before any query runs.

Source

Thrown at internal/storage/issueops/commenter.go:25

	"strings"

	"github.com/steveyegge/beads/internal/storage"
	publicops "github.com/steveyegge/beads/issueops"
)

// ValidateAddCommentRequest applies the request rules every Commenter
// implementation shares.
//
// Blankness is decided on a TRIMMED copy and the request's own Text is left
// alone: a comment of nothing but whitespace carries no information and is
// almost always a shell quoting accident, but a comment that merely begins
// with a newline is a comment.
func ValidateAddCommentRequest(request publicops.AddCommentRequest) error {
	if request.Author == "" {
		return fmt.Errorf("%w: add comment requires an author", storage.ErrValidation)
	}
	if request.IssueID == "" {
		return fmt.Errorf("%w: add comment requires an issue ID", storage.ErrValidation)
	}
	if strings.TrimSpace(request.Text) == "" {
		return fmt.Errorf("%w: comment text cannot be empty", storage.ErrValidation)
	}
	return nil
}

// AddCommentCommitMessage is the history entry a comment records. It is the
// spelling both stores' own AddIssueComment already wrote.
func AddCommentCommitMessage(issueID string) string {
	return "bd: comment " + issueID
}

// ExecuteAddComment appends one comment in tx and reports the durable tables
// changed. It is the store-backed body behind the Commenter accessor; the
// unit-of-work provider has its own, for the reason Lifecycle does.
//
// A comment on an ephemeral row changes only wisp_comments, which

View on GitHub (pinned to 71377f2769)

Solutions

  1. Populate request.IssueID with the target issue's ID before calling AddComment.
  2. Validate the ID is non-empty (and well-formed, e.g. has a prefix) at the call site and report which input was missing.
  3. Classify with errors.Is(err, storage.ErrValidation) and reject the request upstream instead of retrying.

Example fix

// before
req := publicops.AddCommentRequest{Author: author, Text: text}
_ = store.AddComment(ctx, req) // fails: no issue ID

// after
if issueID == "" {
	return fmt.Errorf("usage: bd comment <issue-id> <text>")
}
req := publicops.AddCommentRequest{IssueID: issueID, Author: author, Text: text}
if err := store.AddComment(ctx, req); err != nil { return err }
Defensive patterns

Strategy: validation

Validate before calling

if req.IssueID == "" {
	return errors.New("issue ID is required to add a comment")
}

Try / catch

if err := store.AddComment(ctx, req); err != nil {
	if errors.Is(err, storage.ErrValidation) {
		return fmt.Errorf("invalid request (issue ID?): %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Calling AddComment with publicops.AddCommentRequest{Author: "alice", Text: "note"} and IssueID unset; computing the issue ID from a variable that failed to populate (empty CLI arg, failed lookup); marshalling a request from user input where the issue field was never filled.

Common situations: CLI tools that derive the issue ID from the current branch name or a positional arg and get an empty string; automation that loops over a list where one entry lacks an ID; API consumers omitting the issue_id key in a JSON body.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


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