gastownhall/beads · error

reopen: id must not be empty

Error message

reopen: id must not be empty

What it means

A guard error thrown by the reopen() use case (backing ReopenIssue and ReopenWisp) when the issue ID argument is empty. Like the close path, the library fails fast because an empty ID can never identify a row to reopen.

Source

Thrown at internal/storage/domain/issue.go:1712

		return CloseIssueResult{}, fmt.Errorf("close %s: reload: %w", id, err)
	}
	return CloseIssueResult{
		Issue:  issue,
		Closed: !row.AlreadyClosed,
	}, nil
}

func (u *issueUseCaseImpl) ReopenIssue(ctx context.Context, id string, params ReopenIssueParams, actor string) (ReopenIssueResult, error) {
	return u.reopen(ctx, id, params, actor, false)
}

func (u *issueUseCaseImpl) ReopenWisp(ctx context.Context, id string, params ReopenIssueParams, actor string) (ReopenIssueResult, error) {
	return u.reopen(ctx, id, params, actor, true)
}

func (u *issueUseCaseImpl) reopen(ctx context.Context, id string, params ReopenIssueParams, actor string, useWisp bool) (ReopenIssueResult, error) {
	if id == "" {
		return ReopenIssueResult{}, fmt.Errorf("reopen: id must not be empty")
	}
	if actor == "" {
		return ReopenIssueResult{}, fmt.Errorf("reopen: actor must not be empty")
	}
	row, err := u.issueRepo.Reopen(ctx, id, ReopenRowParams{Reason: params.Reason}, actor, IssueTableOpts{UseWispsTable: useWisp})
	if err != nil {
		return ReopenIssueResult{}, fmt.Errorf("reopen %s: %w", id, err)
	}
	issue, err := u.issueRepo.Get(ctx, id, IssueTableOpts{UseWispsTable: row.IsWisp})
	if err != nil {
		return ReopenIssueResult{}, fmt.Errorf("reopen %s: reload: %w", id, err)
	}
	return ReopenIssueResult{
		Issue:    issue,
		Reopened: row.Updated,
	}, nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Supply a valid issue ID before invoking ReopenIssue.
  2. Filter empty IDs out of batch inputs before looping reopen calls.
  3. Validate id != "" at the boundary (CLI/parser) and emit a clear user-facing message.

Example fix

// before
usecase.ReopenIssue(ctx, id, params, actor) // id == ""
// after
if id == "" { return fmt.Errorf("reopen: issue id required") }
usecase.ReopenIssue(ctx, id, params, actor)
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(id) == "" {
    return fmt.Errorf("reopen: issue id must not be empty")
}

Type guard

func validIssueID(id string) bool { return strings.TrimSpace(id) != "" }

Try / catch

if err := run(); err != nil {
    if strings.Contains(err.Error(), "reopen: id must not be empty") {
        return fmt.Errorf("reopen requires an issue ID")
    }
    return err
}

Prevention

When it happens

Trigger: Calling ReopenIssue / ReopenWisp with id="" — typically an unset variable, empty CLI flag, or blank field from a parsed input passed straight through to the use case.

Common situations: Scripts reopening issues from a list where one entry had an empty ID; misconfigured automation not resolving the issue reference; copy/paste of an invocation with the ID argument dropped.

Related errors


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