gastownhall/beads · error

scanning open wisps for references: %w

Error message

scanning open wisps for references: %w

What it means

This wraps a failure from CommentUseCase().GetCommentsForWisps while reading comments for open wisps (the ephemeral plane) during the sweep reference scan. Wisps' comments live in a separate wisp_comments table; a failure there means the scan cannot prove candidates unreferenced, so the sweep aborts rather than under-scan and delete a bead cited only by a wisp comment.

Source

Thrown at internal/storage/uow/sweeper.go:167

	// BOTH PLANES, and this is not an optimization detail. The not-done set
	// comes from SearchIssues, which merges the durable and wisp planes, so it
	// contains wisps — and their comments live in wisp_comments, which
	// GetCommentsForIssues does not read. Scanning only the durable table left
	// a closed bead cited solely by a comment on an open WISP unprotected, so
	// `bd prune` deleted it on this route and kept it on the other. The
	// contract says an implementation that cannot read the full set must fail
	// the sweep rather than under-scan it.
	//
	// The full id list goes to both reads rather than being partitioned by a
	// plane flag: an id lives in exactly one plane, so at most one side answers
	// for it, and a mis-partition here would silently under-scan again.
	comments, err := uw.CommentUseCase().GetCommentsForIssues(ctx, notDoneIDs)
	if err != nil {
		return nil, fmt.Errorf("scanning open beads for references: %w", err)
	}
	wispComments, err := uw.CommentUseCase().GetCommentsForWisps(ctx, notDoneIDs)
	if err != nil {
		return nil, fmt.Errorf("scanning open wisps for references: %w", err)
	}
	for id, cs := range wispComments {
		comments[id] = append(comments[id], cs...)
	}

	referenced := make(map[string]bool)
	for _, issue := range notDone {
		if issue == nil {
			continue
		}
		matcher.FindAll(issue.Description, referenced)
		matcher.FindAll(issue.Notes, referenced)
		for _, c := range comments[issue.ID] {
			matcher.FindAll(c.Text, referenced)
		}
	}
	return referenced, nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped cause and fix the underlying wisp-comments query failure
  2. Retry the sweep once the database is reachable
  3. Raise query timeouts / batch size limits for very large wisp sets
  4. Run database repair/consistency tooling if corruption is reported
Defensive patterns

Strategy: retry

Validate before calling

// Ensure the wisp comments table is readable before sweeping
rows, err := db.QueryContext(ctx, "SELECT 1 FROM wisp_comments LIMIT 1")
if err != nil {
    return fmt.Errorf("wisp_comments not readable, skip sweep: %w", err)
}
rows.Close()

Try / catch

result, err := sweeper.Sweep(ctx, req)
if err != nil && strings.Contains(err.Error(), "scanning open wisps for references") {
    // wisp-plane comment read failed; restore DB health and retry
    return retrySweepWithBackoff(ctx, req)
}

Prevention

When it happens

Trigger: bd prune/sweep when the batched wisp_comments query for the not-done ID list fails — SQL error on the wisp plane, connection loss, or timeout on a large not-done set.

Common situations: Large numbers of open wisps making the batch query slow; Dolt server restart mid-sweep; schema drift in wisp_comments between beads versions.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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