gastownhall/beads · error

search count %s: %w

Error message

search count %s: %w

What it means

Generic wrapper in scanCountsQuery for QueryContext failures of a counts mega-query; %s is the main table name of the filterTables set (issues, wisps, dependencies...). Called from both fetchCountsByIDs and runSearchQuery, it marks which table family's counts query failed at the engine.

Source

Thrown at internal/storage/domain/db/issue_search_counts.go:183

	if len(whereClauses) > 0 {
		whereSQL = "WHERE " + strings.Join(whereClauses, " AND ")
	}
	orderBy := orderBySQL(filter.SortBy, filter.SortDesc, "i")
	return r.runSearchQuery(ctx, tables, whereSQL, orderBy, searchWindowForFilter(filter).sql, args, includeWispReverseDeps, hydrationFor(filter))
}

//nolint:gosec // G201: SQL fragments are built from hardcoded table names and parameterized filters.
func (r *issueSQLRepositoryImpl) runSearchQuery(ctx context.Context, tables filterTables, whereSQL, orderBySQL, limitSQL string, args []any, includeWispReverseDeps bool, hyd sqlbuild.CountsHydration) ([]*types.IssueWithCounts, error) {
	searchSQL, _ := sqlbuild.SearchCountsSQL(tables, nil, whereSQL, orderBySQL, limitSQL, includeWispReverseDeps, hyd)
	return r.scanCountsQuery(ctx, tables, searchSQL, args, hyd)
}

// scanCountsQuery runs a prebuilt counts mega-query and hydrates each row,
// deduping by issue ID (mirrors issueops.scanCountsRowsInTx).
func (r *issueSQLRepositoryImpl) scanCountsQuery(ctx context.Context, tables filterTables, query string, args []any, hyd sqlbuild.CountsHydration) ([]*types.IssueWithCounts, error) {
	rows, err := r.runner.QueryContext(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("search count %s: %w", tables.Main, err)
	}
	defer func() { _ = rows.Close() }()

	var out []*types.IssueWithCounts
	seen := make(map[string]bool)
	for rows.Next() {
		iwc, scanErr := scanReadyWorkRowWithCounts(rows, hyd)
		if scanErr != nil {
			return nil, scanErr
		}
		if iwc == nil || iwc.Issue == nil {
			continue
		}
		if seen[iwc.Issue.ID] {
			continue
		}
		seen[iwc.Issue.ID] = true
		out = append(out, iwc)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped driver error message for the concrete cause
  2. Run bd migrations / bd doctor to ensure all counts-related tables exist
  3. Reduce hydration breadth (SkipLabels/SkipCounts/Lite) or page size
  4. Increase context timeout; retry transient connection errors

Example fix

// before
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
// after
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
Defensive patterns

Strategy: retry

Validate before calling

for _, t := range []string{"issues", "dependencies", "labels", "wisp_dependencies"} {
    if err := tableExists(ctx, db, t); err != nil {
        return fmt.Errorf("missing table %s: %w", t, err)
    }
}

Try / catch

items, err := store.Search(ctx, q, filter)
if err != nil {
    var derr *driverError
    if errors.As(err, &derr) && derr.Transient() {
        items, err = store.Search(ctx, q, filter)
    }
}

Prevention

When it happens

Trigger: QueryContext fails on any counts mega-query — unknown table/column, engine syntax error, too many bound parameters, connection loss, or context deadline exceeded.

Common situations: Stale schema missing dependencies/wisp tables (outside the tolerated optional paths), very large ID chunks stressing parameter limits, remote Dolt connection drops, or timeouts on wide aggregate queries.

Related errors


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