gastownhall/beads · error
search union with counts: %w
Error message
search union with counts: %w
What it means
Wraps the database/sql error from QueryContext running the combined issues+wisps UNION ALL query. At this point SQL text and args are fully built, so the failure is at the engine: syntax, unknown table/column, connection loss, or placeholder/arg mismatch (iArgs then wArgs must match the leg order).
Source
Thrown at internal/storage/domain/db/issue_search_counts.go:89
if err != nil {
return domain.SearchCountsPage{}, fmt.Errorf("search union with counts (wisps): %w", err)
}
// EACH LEG IS PARENTHESIZED, and it is not decoration. A leg that carries
// its own ORDER BY and LIMIT (legWindowSQL) is a syntax error inside a bare
// UNION ALL — the engine reads the clause as belonging to the union — so the
// parentheses are what let the window be pushed down at all.
//nolint:gosec // G201: subqueries built from hardcoded table names and ? placeholders.
unionSQL := fmt.Sprintf("SELECT id, src FROM ((%s) UNION ALL (%s)) merged %s %s",
iSub, wSub, outerOrderBy, window.sql)
args := make([]any, 0, len(iArgs)+len(wArgs))
args = append(args, iArgs...)
args = append(args, wArgs...)
rows, err := r.runner.QueryContext(ctx, unionSQL, args...)
if err != nil {
return domain.SearchCountsPage{}, fmt.Errorf("search union with counts: %w", err)
}
page, err := scanIDSrcPage(rows)
if err != nil {
return domain.SearchCountsPage{}, fmt.Errorf("search union with counts: %w", err)
}
page.sortGoSide(filter.SortBy, filter.SortDesc)
hasMore, err := page.finishWindow(window)
if err != nil {
return domain.SearchCountsPage{}, err
}
issuesByID, err := r.fetchCountsByIDs(ctx, page.issueIDs, issuesFilterTables, wispDepsExist, hydrationFor(filter))
if err != nil {
return domain.SearchCountsPage{}, fmt.Errorf("search union with counts (hydrate issues): %w", err)
}
wispsByID, err := r.fetchCountsByIDs(ctx, page.wispIDs, wispsFilterTables, true, hydrationFor(filter))
if err != nil && !missingOptionalWispTable(err) {
return domain.SearchCountsPage{}, fmt.Errorf("search union with counts (hydrate wisps): %w", err)View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped driver error for the concrete engine message
- Verify both issues and wisps tables exist and match the expected schema (bd doctor / schema migration)
- Retry on transient network errors; increase context timeout for large searches
- If it's an arg/placeholder mismatch, ensure buildUnionSubquery argument counts match each leg's placeholders
Example fix
// before ctx := context.Background() // after ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // and retry once on transient net errors
Defensive patterns
Strategy: retry
Validate before calling
if err := ensureSchemaCurrent(ctx, db); err != nil {
return fmt.Errorf("run bd migrations first: %w", err)
} Try / catch
page, err := store.SearchWithCounts(ctx, q, filter)
if err != nil && isTransient(err) {
select {
case <-time.After(backoff):
page, err = store.SearchWithCounts(ctx, q, filter)
default:
}
} Prevention
- Use a context timeout sized for large union searches
- Keep schema migrations current for both issues and wisps tables
- Bound retries to transient network errors only
- Monitor DB connection health for remote Dolt servers
When it happens
Trigger: QueryContext on the union SQL fails — driver-level error such as 'no such table', SQL syntax error, too many parameters, or context cancellation/timeouts mid-query.
Common situations: Dolt/SQLite schema missing wisp tables probed as existing earlier, oversized result sets hitting parameter or packet limits, network drop to remote Dolt server, or query cancelled by client timeout.
Related errors
- failed to iterate peers for migration: %w
- db: RawSQL Query: rows: %w
- failed to begin transaction: %w
- failed to recompute is_blocked: %w
- failed to commit is_blocked repairs: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/8163300f78403f1e.
Report an issue: GitHub.