gastownhall/beads · error
reopen %s: reload: %w
Error message
reopen %s: reload: %w
What it means
Reload-failure variant for reopen: issueRepo.Reopen succeeded but the subsequent issueRepo.Get to return the freshly reopened issue failed. The 'reopen <id>: reload:' wrapper distinguishes the failed read-back from a failed reopen.
Source
Thrown at internal/storage/domain/issue.go:1723
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)
}
func (u *issueUseCaseImpl) CountOpenChildren(ctx context.Context, id string) (int, error) {
return u.countOpenChildren(ctx, id, false)
}View on GitHub (pinned to 71377f2769)
Solutions
- Retry; the reopen is committed and effectively idempotent.
- If the cause is not-found, search/list to find which table now holds the row.
- Check storage health and driver errors in the wrapped cause.
- Skip the reload and treat Reopened: true as authoritative if eventual consistency suffices.
Example fix
// before
res, err := usecase.ReopenIssue(ctx, id, params, actor)
// after
if err != nil && strings.Contains(err.Error(), "reload") {
issue, ferr := usecase.GetIssue(ctx, id) // separate re-fetch
_ = issue; _ = ferr
} Defensive patterns
Strategy: try-catch
Validate before calling
if _, err := usecase.GetIssue(ctx, id); err != nil {
return fmt.Errorf("issue %s unreadable before reopen", id)
} Type guard
func isReloadErr(err error) bool {
return err != nil && strings.Contains(err.Error(), ": reload: ")
} Try / catch
res, err := usecase.ReopenIssue(ctx, id, params, actor)
if isReloadErr(err) {
issue, ferr := usecase.GetIssue(ctx, id)
if ferr != nil {
log.Warnf("reload after reopen failed: %v", ferr)
}
return nil // reopen committed
} Prevention
- Avoid concurrent deletion during reopen workflows.
- Retry the reload path once on transient errors.
- Check wisp table state if the reopened row is a wisp.
- Inspect the wrapped cause for the definitive read failure.
When it happens
Trigger: ReopenIssue flow where Reopen commits and u.issueRepo.Get(ctx, id, IssueTableOpts{UseWispsTable: row.IsWisp}) then fails — concurrent delete/move of the row, storage read error, or wisp-table mismatch between write and read.
Common situations: Wisp compaction moving/removing the row between the two calls; transient DB read failure; concurrent agent deleting the issue after it was reopened; replica lag.
Related errors
- close %s: reload: %w
- reopen %s: %w
- failed to check parent issue: %w
- failed to search issues: %w
- failed to load dependencies: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/99be977608e7dc1d.
Report an issue: GitHub.