gastownhall/beads · error

search issues with counts: ephemeral wisp probe: %w

Error message

search issues with counts: ephemeral wisp probe: %w

What it means

SearchIssuesWithCountsInTx wraps a failure of wispsTableEmptyOrMissingInTx when filter.Ephemeral is true. The ephemeral path first probes whether the wisps table is empty or absent before querying it; if that probe query fails (as opposed to reporting empty/missing, which is handled), the search returns this wrapped error.

Source

Thrown at internal/storage/issueops/search_counts.go:22

	"context"
	"database/sql"
	"fmt"
	"sort"

	"github.com/steveyegge/beads/internal/storage/sqlbuild"
	"github.com/steveyegge/beads/internal/types"
)

func SearchIssuesWithCountsInTx(ctx context.Context, tx *sql.Tx, query string, filter types.IssueFilter) ([]*types.IssueWithCounts, error) {
	wispDepsExist, err := optionalTableExistsInTx(ctx, tx, "wisp_dependencies")
	if err != nil {
		return nil, fmt.Errorf("search issues with counts: wisp dependency probe: %w", err)
	}

	if filter.Ephemeral != nil && *filter.Ephemeral {
		empty, probeErr := wispsTableEmptyOrMissingInTx(ctx, tx)
		if probeErr != nil {
			return nil, fmt.Errorf("search issues with counts: ephemeral wisp probe: %w", probeErr)
		}
		if !empty && wispDepsExist {
			wisps, err := runFilterSearchQueryInTx(ctx, tx, query, filter, WispsFilterTables, true)
			if err != nil && !missingOptionalWispTable(err) {
				return nil, err
			}
			if len(wisps) > 0 {
				return finishSearchIssuesWithCounts(wisps, filter)
			}
		}
		// Fall through: the wisps tier is missing/empty or matched no rows.
		// Mirror SearchIssuesInTx / CountIssuesInTx so count-projection searches
		// also surface a durable issues-table row flagged ephemeral=1 instead of
		// dropping it. Use the same IssuesFilterTables query the non-ephemeral
		// path uses, keeping the GH#4387 count/list cardinality parity for
		// searches that project counts (e.g. `bd search --counts --include-infra`).
		out, err := runFilterSearchQueryInTx(ctx, tx, query, filter, IssuesFilterTables, wispDepsExist)
		if err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap probeErr for the real driver error; fix that root cause
  2. Check for concurrent writers/locks on the database file
  3. Retry the search; probe failures are typically transient (locks, connection)
  4. Raise the context timeout for slow or remote databases
  5. Verify DB integrity with bd doctor if failures persist

Example fix

// before: ephemeral search against a DB locked by a bulk import
filter.Ephemeral = &yes
res, err := searchWithCounts(ctx, tx, q, filter) // probe fails on lock
// after: wait/retry around the writer, or run after import completes
<-importDone
res, err := searchWithCounts(ctx, tx, q, filter)
Defensive patterns

Strategy: retry

Validate before calling

// ensure the wisps table is at least introspectable before an ephemeral search
var name string
err := tx.QueryRowContext(ctx,
    "SELECT name FROM sqlite_master WHERE type='table' AND name='wisps'").Scan(&name)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
    return fmt.Errorf("wisps introspection broken: %w", err)
}

Type guard

func isEphemeralProbeErr(err error) bool {
    return err != nil && strings.Contains(err.Error(),
        "search issues with counts: ephemeral wisp probe: ")
}

Try / catch

res, err := issueops.SearchIssuesWithCountsInTx(ctx, tx, q, filter)
if err != nil && isEphemeralProbeErr(err) {
    time.Sleep(100 * time.Millisecond) // lock contention often clears
    res, err = issueops.SearchIssuesWithCountsInTx(ctx, tx, q, filter)
}

Prevention

When it happens

Trigger: Calling SearchIssuesWithCountsInTx with filter.Ephemeral = &true where the wisps emptiness probe SQL errors — DB locked, connection dropped, ctx cancelled, or catalog introspection failure.

Common situations: Concurrent writer holding the SQLite write lock; remote Dolt server outage; deadline too short for the probe plus search; corrupted DB.

Understand the failure class

Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.

Related errors


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