gastownhall/beads · error
search %s (hydrate): rows: %w
Error message
search %s (hydrate): rows: %w
What it means
searchTablePatternBT wraps the error returned by fetchRows.Err() after the Pattern B batch fetch finished iterating: the driver reported a failure while streaming rows (not a scan error, not an initial query error). This catches mid-result-set failures like dropped connections or backend errors surfaced during row retrieval.
Source
Thrown at internal/storage/issueops/search.go:474
proj.columns(tables), fetchFrom, strings.Join(placeholders, ","))
fetchRows, err := tx.QueryContext(ctx, fetchSQL, fetchArgs...)
if err != nil {
return nil, fmt.Errorf("search %s (hydrate): %w", tables.Main, err)
}
itemMap := make(map[string]T, len(ids))
for fetchRows.Next() {
item, scanErr := proj.scan(fetchRows)
if scanErr != nil {
_ = fetchRows.Close()
return nil, fmt.Errorf("search %s (hydrate): scan: %w", tables.Main, scanErr)
}
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
}View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped rows error; retry the search if it was transient (connection reset, timeout)
- Raise the context timeout or remove the deadline for large fetches
- Reduce filter.Limit to shrink the batch fetch
- Check server logs (Dolt/SQLite) for query aborts or kills around the timestamp
- Verify network stability to the database host if using a remote Dolt server
Example fix
// before: short default timeout killed a large batch fetch mid-stream ctx := context.Background() // after: bound generously for large result sets ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() rows, err := searchIssues(ctx, tx, q, filter)
Defensive patterns
Strategy: retry
Validate before calling
// pre-check connection health before large batch fetches
if err := tx.PingContext(ctx); err != nil {
return fmt.Errorf("connection unhealthy before search: %w", err)
}
if ctx.Err() != nil { return ctx.Err() } Type guard
func isRowsIterationErr(err error) bool {
return err != nil && strings.Contains(err.Error(), ": rows: ")
} Try / catch
var res []Issue
var err error
for attempt := 0; attempt < 3; attempt++ {
res, err = searchIssues(ctx, tx, q, filter)
if err == nil || !isRowsIterationErr(err) { break }
time.Sleep(backoff(attempt)) // retry transient mid-stream failures
}
if err != nil { return err } Prevention
- Use generous context timeouts for large fetches
- Enable connection retry/backoff at the driver layer
- Monitor network stability to remote Dolt servers
- Reduce batch sizes (lower filter.Limit) on flaky links
When it happens
Trigger: During iteration of the WHERE id IN (...) fetch, the underlying connection breaks, the server aborts the query, or ctx is cancelled between Next() calls — the driver sets an error on the Rows that only rows.Err() surfaces after Close.
Common situations: Network drop to a remote Dolt server mid-fetch; query killed by a server-side timeout; ctx deadline exceeded on a slow disk; process memory pressure causing driver-level failures.
Related errors
- iterate dependency sources: %w
- iterate dependency targets: %w
- count edges in %s: rows: %w
- db: ChildCounterSQLRepository.NextChildID: rows: %w
- db: CommentSQLRepository.CountsByIssueIDs: rows: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/b17a359797b2e8cb.
Report an issue: GitHub.