gastownhall/beads · error
search %s: rows: %w
Error message
search %s: rows: %w
What it means
Raised when rows.Err() returns non-nil after fully iterating the search result set. This is the idiomatic database/sql check for errors that occurred asynchronously during streaming — typically the connection dropping or the server aborting mid-iteration, not an error at query start.
Source
Thrown at internal/storage/domain/db/issue_search.go:279
}
var issues []*types.Issue
seen := make(map[string]bool)
for rows.Next() {
issue, scanErr := scanIssue(rows)
if scanErr != nil {
_ = rows.Close()
return domain.SearchPage{}, fmt.Errorf("search %s: scan: %w", tables.Main, scanErr)
}
if seen[issue.ID] {
continue
}
seen[issue.ID] = true
issues = append(issues, issue)
}
_ = rows.Close()
if err := rows.Err(); err != nil {
return domain.SearchPage{}, fmt.Errorf("search %s: rows: %w", tables.Main, err)
}
sortRowsGoSide(issues, func(i *types.Issue) string { return i.ID }, filter.SortBy, filter.SortDesc)
items, hasMore, err := finishWindow(issues, window)
if err != nil {
return domain.SearchPage{}, err
}
if err := r.hydrateIssues(ctx, items, tables, filter.IncludeDependencies, filter.SkipLabels); err != nil {
return domain.SearchPage{}, fmt.Errorf("search %s: hydrate: %w", tables.Main, err)
}
return domain.SearchPage{Items: items, HasMore: hasMore}, nil
}
func (r *issueSQLRepositoryImpl) scanFilterIDs(ctx context.Context, selectKw, fromSQL, whereSQL string, args []any, filter types.IssueFilter, tables filterTables) ([]string, bool, error) {
orderBy := orderBySQL(filter.SortBy, filter.SortDesc, tables.Main)
window := searchWindowForFilter(filter)View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped error to distinguish connection loss from driver I/O failure.
- Retry the search; transient connection drops usually succeed on a second attempt.
- Reduce result-set size with a tighter IssueFilter (limit/status filters) so iteration completes before timeout.
- For remote Dolt, increase connection idle/read timeouts in the driver configuration.
Example fix
// before
page, err := store.Search(ctx, q, types.IssueFilter{}) // huge result set, drops mid-stream
// after
page, err := store.Search(ctx, q, types.IssueFilter{Status: "open", Limit: 100}) Defensive patterns
Strategy: retry
Validate before calling
if err := ctx.Err(); err != nil { return err } // ensure context is still live before searching
// prefer filtered queries over unbounded scans Type guard
func isRowsIterationError(err error) bool {
return err != nil && strings.Contains(err.Error(), ": rows: ")
} Try / catch
var page domain.SearchPage
err := retry.Do(3, backoff, func() error {
var e error
page, e = store.Search(ctx, q, filter)
if e != nil && isRowsIterationError(e) {
return e // transient stream failure; retry
}
return retry.Stop(e)
}) Prevention
- Use Limit/status filters so result streams stay small.
- Increase connection read/idle timeouts for remote Dolt servers.
- Keep the database on reliable local storage.
- Retry transient stream errors with bounded backoff.
When it happens
Trigger: Iterating a large search result when the underlying connection is lost, the driver hits I/O failure reading the next batch, or the database process is killed while rows are still open.
Common situations: Searching very large issue sets over a remote Dolt server whose connection times out mid-stream; SQLite file unmounted or network filesystem dropped during iteration; OOM-killer terminating the DB process during a big search.
Related errors
- search %s (id scan): rows: %w
- get next child ID: iterate children: %w
- iterate dependency sources: %w
- iterate dependency targets: %w
- count edges in %s: rows: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/3c9ca1f2a6e70f78.
Report an issue: GitHub.