gastownhall/beads · error

search %s: %w

Error message

search %s: %w

What it means

This error wraps a failure from the underlying SQL driver when executing the issue/wisp search query via runner.QueryContext. The beads storage layer parameterizes all filters and only interpolates fixed table names, so any error here originates from the database engine itself (syntax, connection, schema) and is reported as "search <table>: <driver error>". It propagates up through SearchAcrossIssuesAndWisps to the caller's IssueFilter-based search API.

Source

Thrown at internal/storage/domain/db/issue_search.go:260

			return domain.SearchPage{}, nil
		}
		byID, err := r.fetchIssuesByIDs(ctx, ids, tables, filter)
		if err != nil {
			return domain.SearchPage{}, fmt.Errorf("search %s (hydrate): %w", tables.Main, err)
		}
		return domain.SearchPage{Items: orderByIDs(ids, byID), HasMore: hasMore}, nil
	}

	orderBy := orderBySQL(filter.SortBy, filter.SortDesc, "")
	window := searchWindowForFilter(filter)

	//nolint:gosec // G201: SQL fragments from fixed table names and parameterized filters.
	querySQL := fmt.Sprintf(`%s%s FROM %s %s %s %s %s`,
		selectKw, issueSelectColumns, plan.FromSQL, sqlbuild.LeaseJoin(tables.Main), whereSQL, orderBy, window.sql)

	rows, err := r.runner.QueryContext(ctx, querySQL, args...)
	if err != nil {
		return domain.SearchPage{}, fmt.Errorf("search %s: %w", tables.Main, err)
	}

	var issues []*types.Issue
	seen := make(map[string]bool)
	for rows.Next() {
		issue, scanErr := scanIssue(rows)
		if scanErr != nil {
			_ = rows.Close()
			return domain.SearchPage{}, fmt.Errorf("search %s: scan: %w", tables.Main, scanErr)
		}
		if seen[issue.ID] {
			continue
		}
		seen[issue.ID] = true
		issues = append(issues, issue)
	}
	_ = rows.Close()
	if err := rows.Err(); err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped driver error (errors.Unwrap / %w chain) to identify the root cause (no such table, locked, I/O error).
  2. Run `bd doctor` to validate database health and schema; re-migrate or rebuild the database if tables are missing.
  3. Close other bd processes holding a lock on the database and retry.
  4. Check disk space and file permissions on the .beads directory.
  5. Ensure the context passed to Search has an adequate deadline, or pass context.Background() for interactive searches.

Example fix

// before
page, err := store.Search(ctx, query, filter) // fails: database is locked
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := bd.Doctor(ctx, dbPath); err != nil { /* repair DB first */ }
page, err := store.Search(ctx, query, filter)
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := os.Stat(dbPath); err != nil { return fmt.Errorf("database missing: %w", err) }
if err := bd.Doctor(ctx, dbPath); err != nil { return err }

Type guard

func isSearchQueryError(err error) bool {
	return err != nil && strings.Contains(err.Error(), "search ")
}

Try / catch

page, err := store.Search(ctx, q, filter)
if err != nil {
	var root error
	for unwrapped := err; unwrapped != nil; unwrapped = errors.Unwrap(unwrapped) {
		root = unwrapped
	}
	if dberrors.IsLocked(root) || errors.Is(root, context.DeadlineExceeded) {
		// retry with backoff
	}
	return fmt.Errorf("search failed: %w", err)
}

Prevention

When it happens

Trigger: Calling Search / SearchAcrossIssuesAndWisps when the database rejects the generated search SQL: locked or unreadable SQLite/Dolt database, missing issues or wisps table, driver-level connection loss mid-query, or a context cancelled/expired before the query completes.

Common situations: Corrupt or migrated-away .beads database file; running a newer bd binary against an older DB schema lacking a table the search expects; WAL lock contention from a concurrent bd process; disk-full or permission errors on the DB file; long-running search killed by context deadline in a daemon.

Related errors


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