gastownhall/beads · error
reopen %s: %w
Error message
reopen %s: %w
What it means
This error wraps any failure from u.issueRepo.Reopen in the reopen() use case — the storage-level state transition from closed back to open failed. The ID is embedded in the message and the underlying repository error (not-found, storage failure) is chained via %w.
Source
Thrown at internal/storage/domain/issue.go:1719
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
}
func (u *issueUseCaseImpl) ClaimIssueIfOpen(ctx context.Context, id, actor string) (ClaimResult, error) {
return u.claim(ctx, id, actor, false)
}
func (u *issueUseCaseImpl) ClaimWispIfOpen(ctx context.Context, id, actor string) (ClaimResult, error) {
return u.claim(ctx, id, actor, true)
}View on GitHub (pinned to 71377f2769)
Solutions
- Confirm the issue exists and is currently closed via Get before reopening.
- Check the wrapped cause: not-found means bad ID or wrong table routing; fix the ID or wisp flag.
- Retry on transient storage errors (locks, connectivity).
- If wisp-related, ensure the wisp routing flag matches where the row actually lives.
Example fix
// before
_, err := usecase.ReopenIssue(ctx, id, params, actor)
// after
if _, gerr := usecase.GetIssue(ctx, id); gerr != nil {
return fmt.Errorf("cannot reopen %s: %w", id, gerr)
}
_, err := usecase.ReopenIssue(ctx, id, params, actor) Defensive patterns
Strategy: validation
Validate before calling
issue, err := usecase.GetIssue(ctx, id)
if err != nil {
return fmt.Errorf("cannot reopen %s: %w", id, err)
}
if issue.Status != StatusClosed {
return fmt.Errorf("%s is not closed", id)
} Type guard
func isNotFound(err error) bool {
return errors.Is(err, ErrNotFound)
} Try / catch
_, err := usecase.ReopenIssue(ctx, id, params, actor)
if err != nil {
if isNotFound(err) {
return fmt.Errorf("issue %s not found", id)
}
if isTransient(err) {
return retryReopen(ctx, id, params, actor)
}
return err
} Prevention
- Verify existence and closed status before reopening.
- Match wisp routing flags to where the row lives.
- Retry transient storage failures with backoff.
- Keep IDs sourced from fresh list output.
When it happens
Trigger: Calling ReopenIssue where issueRepo.Reopen(ctx, id, ReopenRowParams, actor, opts) errors: ID not found in the routed table (main vs wisps per useWisp), database failure, or a driver/constraint rejection.
Common situations: Reopening an already-deleted or mistyped issue ID; the issue is a wisp but the call routed to the main table (or vice versa); transient DB lock/connectivity failure during the update.
Related errors
- reopen %s: reload: %w
- failed to check parent issue: %w
- failed to search issues: %w
- failed to load dependencies: %w
- resolving %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/6e59472b8c139325.
Report an issue: GitHub.