gastownhall/beads · error

search union with counts (hydrate issues): %w

Error message

search union with counts (hydrate issues): %w

What it means

Wraps failures from fetchCountsByIDs when hydrating counts (labels, dependencies, etc.) for the issue IDs on the union page. The hydration uses the by-IDs counts mega-query with chunked IDs; failures here mean the follow-up hydration SQL failed, not the main search.

Source

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

	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)
	}

	out := reassembleBySrc(page.ordered, issuesByID, wispsByID)
	return domain.SearchCountsPage{Items: out, HasMore: hasMore}, nil
}

// hydrationFor reads the hydration opt-outs off a search filter, matching the
// store-backed path's helper of the same name (internal/storage/issueops).
// Both bodies must read the same fields off the same filter or a caller that
// set one would get different columns from the two backends.
func hydrationFor(filter types.IssueFilter) sqlbuild.CountsHydration {
	return sqlbuild.CountsHydration{SkipLabels: filter.SkipLabels, SkipCounts: filter.SkipCounts, Lite: filter.Lite}
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the inner 'search count issues' error for the engine message
  2. Reduce page size / skip hydration via filter.SkipCounts or filter.Lite to shrink the mega-query
  3. Verify dependencies/labels tables exist and schema matches
  4. Retry on transient connection errors

Example fix

// before
filter := types.IssueFilter{...}
// after
filter.SkipCounts = true // or filter.Lite = true to lighten hydration
Defensive patterns

Strategy: fallback

Validate before calling

if page.ItemCount > largePageThreshold {
    filter.Lite = true // shrink hydration before calling
}

Try / catch

page, err := store.SearchWithCounts(ctx, q, filter)
if err != nil && strings.Contains(err.Error(), "hydrate issues") {
    filter.SkipCounts = true
    page, err = store.SearchWithCounts(ctx, q, filter) // degrade gracefully
}

Prevention

When it happens

Trigger: After the union page is built, fetchCountsByIDs for page.issueIDs fails — engine error in the counts mega-query, context cancellation, or chunked placeholder issues.

Common situations: Timeouts during hydration of large pages, schema issues in dependency/label tables, or DB connection drops between the search and hydration queries.

Related errors


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