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

  1. Unwrap the inner error to find which hydration query failed and fix that root cause
  2. Run schema migration so all auxiliary tables exist (bd migrate / bd doctor)
  3. Reduce filter.Limit / set MaxRows to shrink hydration work if the cause is a timeout
  4. Retry on a fresh transaction if the tx was invalidated by an earlier statement error
  5. 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

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


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