gastownhall/beads · error
close %s: reload: %w
Error message
close %s: reload: %w
What it means
This error is produced by the issue close use case when the issue was successfully closed (or verified already closed) but the subsequent read-back of the issue record via issueRepo.Get failed. The 'reload:' prefix distinguishes this post-close fetch failure from the close operation itself, which succeeded. The returned error wraps the underlying Get error (e.g. not-found, storage failure) with 'close <id>: reload:'.
Source
Thrown at internal/storage/domain/issue.go:1676
// CloseWispChecked is the wisp twin of CloseIssueChecked.
func (u *issueUseCaseImpl) CloseWispChecked(ctx context.Context, id string, params CloseIssueParams, actor string, force bool) (CloseIssueResult, error) {
return u.closeChecked(ctx, id, params, actor, force, true)
}
func (u *issueUseCaseImpl) closeChecked(ctx context.Context, id string, params CloseIssueParams, actor string, force, useWisp bool) (CloseIssueResult, error) {
if id == "" {
return CloseIssueResult{}, fmt.Errorf("close: id must not be empty")
}
if actor == "" {
return CloseIssueResult{}, fmt.Errorf("close: actor must not be empty")
}
row, err := u.issueRepo.CloseChecked(ctx, id, CloseRowParams{Reason: params.Reason, Session: params.Session}, actor, force)
if err != nil {
return CloseIssueResult{}, fmt.Errorf("close %s: %w", id, err)
}
issue, err := u.issueRepo.Get(ctx, id, IssueTableOpts{UseWispsTable: row.IsWisp || useWisp})
if err != nil {
return CloseIssueResult{}, fmt.Errorf("close %s: reload: %w", id, err)
}
return CloseIssueResult{Issue: issue, Closed: !row.AlreadyClosed, OpenChildren: row.OpenChildren}, nil
}
func (u *issueUseCaseImpl) close(ctx context.Context, id string, params CloseIssueParams, actor string, useWisp bool) (CloseIssueResult, error) {
if id == "" {
return CloseIssueResult{}, fmt.Errorf("close: id must not be empty")
}
if actor == "" {
return CloseIssueResult{}, fmt.Errorf("close: actor must not be empty")
}
row, err := u.issueRepo.Close(ctx, id, CloseRowParams{Reason: params.Reason, Session: params.Session}, actor, IssueTableOpts{UseWispsTable: useWisp})
if err != nil {
return CloseIssueResult{}, fmt.Errorf("close %s: %w", id, err)
}
issue, err := u.issueRepo.Get(ctx, id, IssueTableOpts{UseWispsTable: row.IsWisp})
if err != nil {
return CloseIssueResult{}, fmt.Errorf("close %s: reload: %w", id, err)View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped error: if it is a not-found error, the issue was closed then deleted concurrently — retry or treat the close as effective and re-fetch by listing.
- Verify database/storage connectivity and retry the whole CloseIssue call; the close itself is idempotent (AlreadyClosed is reported).
- Inspect wisp table routing: if row.IsWisp was true, confirm the wisp still exists in the wisps table or re-run after compaction settles.
- Log/report the underlying Get error if it persists, since close state may have changed without a verifiable result.
Example fix
// before: assuming close succeeded means reload will succeed
result, err := u.CloseIssue(ctx, id, params)
// after: handle the reload failure distinctly
closeErr := &CloseReloadError{ID: id, Err: err}
if errors.Is(err, ErrNotFound) {
// issue closed and concurrently removed; proceed without reload
} Defensive patterns
Strategy: try-catch
Validate before calling
if _, err := usecase.GetIssue(ctx, id); err != nil {
// cannot even read the issue; close will likely fail or reload fail
return fmt.Errorf("issue %s unreadable: %w", id, err)
} Type guard
func hasIssue(issue Issue, ok bool) bool { return ok && issue.ID != "" } Try / catch
res, err := usecase.CloseIssue(ctx, id, params)
if err != nil {
if strings.Contains(err.Error(), "reload:") {
// close committed; handle re-fetch failure separately
log.Warnf("close of %s committed but reload failed: %v", id, err)
} else {
return err
}
} Prevention
- Avoid concurrent delete-and-close of the same issue; serialize mutations per ID.
- Wrap close+reload in retries for transient storage errors.
- Monitor wisp compaction windows if you close wisps frequently.
- Always inspect the wrapped cause with errors.Is/errors.As rather than string matching where possible.
When it happens
Trigger: Calling CloseIssue (or CloseWisp, which routes through closeWithRowChecked) where u.issueRepo.CloseChecked succeeds but the follow-up u.issueRepo.Get(ctx, id, ...) returns an error — e.g. the row was deleted concurrently between close and reload, or a storage/database error occurs on read, or the wisp-table routing (row.IsWisp || useWisp) points at a table where the row no longer exists.
Common situations: Concurrent deletion of the issue between close and reload; database connectivity blips during the read; wisp-vs-main table inconsistency where the closed row was a wisp that got compacted/merged before Get ran; permissions or driver errors surfacing through the repository layer.
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/d0607fbeacebd400.
Report an issue: GitHub.