gastownhall/beads · error
search %s (pattern B): %w
Error message
search %s (pattern B): %w
What it means
searchTablePatternBT wraps a failure from the projection's hydrate callback after a successful Pattern B id scan + batch fetch. The rows were retrieved and reordered, but post-fetch enrichment (dependencies, labels, counts, etc.) inside the same transaction failed. Identical root-cause family to the plain-path hydrate wrapper, but tagged '(pattern B)'.
Source
Thrown at internal/storage/issueops/search.go:487
}
itemMap[proj.id(item)] = item
}
_ = fetchRows.Close()
if err := fetchRows.Err(); err != nil {
return nil, fmt.Errorf("search %s (hydrate): rows: %w", tables.Main, err)
}
// Reorder to preserve the id-scan ORDER BY.
results := make([]T, 0, len(ids))
for _, id := range ids {
if item, ok := itemMap[id]; ok {
results = append(results, item)
}
}
if proj.hydrate != nil && len(results) > 0 {
if err := proj.hydrate(ctx, tx, tables, results, filter); err != nil {
return nil, fmt.Errorf("search %s (pattern B): %w", tables.Main, err)
}
}
return results, nil
}
// EffectiveSearchLimit returns the SQL LIMIT to apply given a caller-supplied
// Limit and a defensive MaxRows cap. Semantics (architecture be-jp5s D1/R-03):
//
// - limit=0, maxRows=0: returns 0 (no LIMIT clause; unlimited)
// - limit=N, maxRows=0: returns N (today's --limit behavior)
// - limit=0, maxRows=M: returns M+1 (detect overage at cap+1)
// - limit=N, maxRows=M: returns N if N<=M, else M+1
//
// Callers issue LIMIT cap+1 specifically so that EnforceMaxRowsCap can detect
// overage by comparing len(scanned rows) to MaxRows. The returned int is the
// LIMIT value; treat 0 as "do not emit a LIMIT clause".
func EffectiveSearchLimit(limit, maxRows int) int {View on GitHub (pinned to 71377f2769)
Solutions
- Unwrap the inner error to find which hydration query failed and fix that root cause
- Run schema migration so all auxiliary tables exist (bd migrate / bd doctor)
- Reduce filter.Limit / set MaxRows to shrink hydration work if the cause is a timeout
- Retry on a fresh transaction if the tx was invalidated by an earlier statement error
- Set the relevant SkipLabels/SkipCounts/Lite filter flags to bypass hydration legs you do not need
Example fix
// before: hydrating deps+labels+counts for 10k rows under a 2s deadline filter.Limit = 10000 // after: trim the work ctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() filter.Limit = 100 filter.SkipCounts = true issues, err := searchIssues(ctx, tx, q, filter)
Defensive patterns
Strategy: try-catch
Validate before calling
// confirm hydration prerequisites exist before a Pattern B search
for _, t := range []string{"dependencies", "labels", "wisp_dependencies"} {
var one int
err := tx.QueryRowContext(ctx,
"SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", t).Scan(&one)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return fmt.Errorf("schema probe failed: %w", err)
}
} Type guard
func isPatternBHydrateErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "(pattern B): ")
} Try / catch
res, err := searchIssues(ctx, tx, q, filter)
if err != nil && isPatternBHydrateErr(err) {
// degrade gracefully: retry without hydration legs
filter.SkipCounts = true
filter.SkipLabels = true
res, err = searchIssues(ctx, tx, q, filter)
} Prevention
- Migrate schema so optional hydration tables exist
- Trim hydration work with SkipLabels/SkipCounts/Lite when possible
- Size deadlines to the hydrated row count
- Retry on a fresh transaction if a prior statement poisoned the tx
When it happens
Trigger: searchTablePatternBT completed the id scan and batch fetch, results are non-empty, proj.hydrate != nil, and the hydration queries error — missing auxiliary table, poisoned tx, or ctx cancellation during hydration.
Common situations: Schema version drift dropping a table the hydrator joins (e.g. wisp_dependencies on older DBs); lock contention during hydration; deadline exceeded hydrating many rows after a large limit.
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
- ready work union with counts: hydrate wisps: %w
- get dependencies: fetch issues: %w
- get dependents: fetch issues: %w
- get dependency counts: probe: %w
- get dependency counts (blockers from %s): %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/ebf5617a1632501e.
Report an issue: GitHub.