gastownhall/beads · error
collectConnected (wisps): %w
Error message
collectConnected (wisps): %w
What it means
This error wraps a failure from depRepo.ListByIssueIDs with UseWispsTable=true and Direction=DepDirectionBoth in collectConnectedIssues. A missing wisps table is tolerated (IsTableNotExist short-circuits); any other failure aborts the delete or preview, since connected-issue detection would be incomplete.
Source
Thrown at internal/storage/domain/issue_delete.go:350
return preview, nil
}
func (u *issueUseCaseImpl) collectConnectedIssues(
ctx context.Context, allIDs []string, deletedSet map[string]bool,
) (map[string]*types.Issue, map[string]bool, error) {
out := map[string]*types.Issue{}
isWisp := map[string]bool{}
if len(allIDs) == 0 {
return out, isWisp, nil
}
issueRes, err := u.depRepo.ListByIssueIDs(ctx, allIDs, DepListOpts{Direction: DepDirectionBoth})
if err != nil {
return nil, nil, fmt.Errorf("collectConnected (issues): %w", err)
}
wispRes, err := u.depRepo.ListByIssueIDs(ctx, allIDs, DepListOpts{Direction: DepDirectionBoth, UseWispsTable: true})
if err != nil && !dberrors.IsTableNotExist(err) {
return nil, nil, fmt.Errorf("collectConnected (wisps): %w", err)
}
neighbors := map[string]bool{}
accumulate := func(m map[string][]*types.Dependency) {
for _, deps := range m {
for _, d := range deps {
for _, candidate := range [2]string{d.IssueID, d.DependsOnID} {
if candidate == "" || deletedSet[candidate] {
continue
}
neighbors[candidate] = true
}
}
}
}
accumulate(issueRes.Outgoing)
accumulate(issueRes.Incoming)
accumulate(wispRes.Outgoing)View on GitHub (pinned to 71377f2769)
Solutions
- Resolve the wrapped driver error first (connectivity, locks, timeouts).
- Retry the operation; reads are safe and deletes are idempotent per ID.
- Run migrations if the wisps schema drifted from the installed version.
- If wisps are unused, the expected table-not-exist path is skipped silently — a surfaced error means something else failed.
Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure wisps schema is migrated or intentionally absent before deletes:
if err := migrate(ctx); err != nil {
return fmt.Errorf("wisps schema check failed: %w", err)
} Type guard
if dberrors.IsTableNotExist(errors.Unwrap(err)) {
return nil // tolerated: no wisps dependency table
}
var dbErr *dberrors.DBError
if errors.As(err, &dbErr) {
// real driver failure — handle or propagate
} Try / catch
res, err := uc.DeleteWisps(ctx, ids, opts)
if err != nil && strings.Contains(err.Error(), "collectConnected (wisps)") {
if !dberrors.IsTableNotExist(errors.Unwrap(err)) {
return fmt.Errorf("wisp connectivity scan failed: %w", err)
}
} Prevention
- Run migrations so wisps schema matches the binary
- Distinguish tolerated IsTableNotExist from real failures
- Retry transient failures — deletes are idempotent per ID
- Avoid concurrent wisp writers during large deletes
When it happens
Trigger: deleteMany (with text-reference rewriting) or previewDelete when the wisps dependency table exists but the bidirectional query fails: connection loss, timeout, schema/scan mismatch.
Common situations: Wisp table locked by concurrent writers; upgrade-related schema drift; transient DB failures during large cascade operations.
Related errors
- delete: list wisp dependents: %w
- collectConnected (issues): %w
- no store is open for this workspace
- load dependency records: %w
- load wisp dependency counts: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a44896bf8e95f634.
Report an issue: GitHub.