gastownhall/beads · error

reopen: actor and issue ID must not be empty

Error message

reopen: actor and issue ID must not be empty

What it means

validateReopenRequest requires a non-empty Actor and IssueID on every ReopenRequest, wrapping this rejection in publicops.ErrValidation. Reopening records who performed the action and on which issue.

Solutions

  1. Set request.Actor to the acting identity before calling Reopen
  2. Set request.IssueID to the target issue key
  3. Guard the call site: skip or fix requests with empty fields
  4. Trace where the empty value originates (config, env, parsing) and add a default or error

Example fix

// before
req := publicops.ReopenRequest{IssueID: id}
// after
req := publicops.ReopenRequest{Actor: actor, IssueID: id}
if req.Actor == "" || req.IssueID == "" { return fmt.Errorf("reopen aborted: missing actor/id") }
Defensive patterns

Strategy: validation

Validate before calling

func validReopen(req publicops.ReopenRequest) error {
    if req.Actor == "" { return fmt.Errorf("actor required") }
    if req.IssueID == "" { return fmt.Errorf("issue ID required") }
    return nil
}

Type guard

func reopenRequestComplete(req publicops.ReopenRequest) bool {
    return req.Actor != "" && req.IssueID != ""
}

Try / catch

if err := uc.Reopen(ctx, req); err != nil {
    if errors.Is(err, publicops.ErrValidation) {
        // input bug: populate Actor/IssueID, do not retry blindly
        return err
    }
    return err
}

Prevention

When it happens

Trigger: Calling Reopen with a publicops.ReopenRequest where request.Actor == "" or request.IssueID == "".

Common situations: A reopen automation with no actor identity configured, IDs parsed from input that yielded empty strings, or a request struct partially populated after copy-paste from another operation.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at internal/storage/uow/issue_operations.go:581

	for _, key := range keys {
		if err := storage.ValidateMetadataKey(key); err != nil {
			return err
		}
		if !json.Valid(metadata.Set[key]) {
			return fmt.Errorf("metadata value for key %q is not valid JSON", key)
		}
	}
	for _, key := range metadata.Unset {
		if err := storage.ValidateMetadataKey(key); err != nil {
			return err
		}
	}
	return nil
}

func validateReopenRequest(request publicops.ReopenRequest) error {
	if request.Actor == "" || request.IssueID == "" {
		return validationError(fmt.Errorf("reopen: actor and issue ID must not be empty"))
	}
	return nil
}

func semanticIssueEqual(left, right *types.Issue) bool {
	if left == nil || right == nil {
		return left == right
	}
	leftCopy := *left
	rightCopy := *right
	leftCopy.UpdatedAt, rightCopy.UpdatedAt = time.Time{}, time.Time{}
	leftCopy.RowVersion, rightCopy.RowVersion = 0, 0
	return reflect.DeepEqual(leftCopy, rightCopy)
}

View on GitHub (pinned to 71377f2769)