{"record":{"id":"d22f049506baa1ca","repo":"gastownhall/beads","slug":"search-s-id-scan-rows-w","errorCode":null,"errorMessage":"search %s (id scan): rows: %w","messagePattern":"search (.+?) \\(id scan\\): rows: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/issue_search.go","lineNumber":317,"sourceCode":"\tidQuery := fmt.Sprintf(`%s%s.id FROM %s %s %s %s`,\n\t\tselectKw, tables.Main, fromSQL, whereSQL, orderBy, window.sql)\n\n\trows, err := r.runner.QueryContext(ctx, idQuery, args...)\n\tif err != nil {\n\t\treturn nil, false, fmt.Errorf(\"search %s (id scan): %w\", tables.Main, err)\n\t}\n\tdefer func() { _ = rows.Close() }()\n\n\tvar ids []string\n\tfor rows.Next() {\n\t\tvar id string\n\t\tif err := rows.Scan(&id); err != nil {\n\t\t\treturn nil, false, fmt.Errorf(\"search %s (id scan): scan: %w\", tables.Main, err)\n\t\t}\n\t\tids = append(ids, id)\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn nil, false, fmt.Errorf(\"search %s (id scan): rows: %w\", tables.Main, err)\n\t}\n\n\tsortRowsGoSide(ids, func(id string) string { return id }, filter.SortBy, filter.SortDesc)\n\treturn finishWindow(ids, window)\n}\n\nfunc (r *issueSQLRepositoryImpl) hydrateIssues(ctx context.Context, issues []*types.Issue, tables filterTables, includeDeps bool, skipLabels bool) error {\n\tif len(issues) == 0 {\n\t\treturn nil\n\t}\n\n\tids := make([]string, len(issues))\n\tfor i, issue := range issues {\n\t\tids[i] = issue.ID\n\t}\n\n\tif !skipLabels {\n\t\tlabelMap, err := r.getLabelsFromTable(ctx, tables.Labels, ids)","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/issue_search.go#L299-L335","documentation":"Raised when rows.Err() is non-nil after iterating all id rows in scanFilterIDs. As with the full-search variant, this catches errors surfaced during row streaming (connection drop, driver I/O) rather than at query execution.","triggerScenarios":"Streaming the id list for a filtered search when the connection dies mid-iteration: network loss to a remote Dolt server, DB process termination, or file I/O failure on an embedded database.","commonSituations":"Very large issue tables scanned over flaky network connections; embedded SQLite on network-attached storage; laptop sleep / network switch during a long scan.","solutions":["Check the wrapped error for connection vs I/O cause and retry the search.","Move large workloads close to the database (avoid NAS-hosted .beads directories).","Narrow the filter (status, limit, label) to reduce streamed rows.","Increase driver timeout/read settings for remote Dolt connections."],"exampleFix":"// before\ndb on NAS path; ids scan drops mid-iteration\n// after\nmove .beads to local disk or run bd against a local dolt server","handlingStrategy":"retry","validationCode":"if err := ctx.Err(); err != nil { return err }\n// keep DB on local storage; check network before remote dolt server calls\nif err := pingDoltServer(ctx); err != nil { return err }","typeGuard":"func isIDScanRowsError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"(id scan): rows: \")\n}","tryCatchPattern":"err := retry.Do(3, backoff, func() error {\n\t_, e := store.Search(ctx, q, filter)\n\tif e != nil && isIDScanRowsError(e) {\n\t\treturn e // transient stream drop\n\t}\n\treturn retry.Stop(e)\n})","preventionTips":["Avoid NAS/network-attached locations for embedded databases.","Bound streamed id scans with filters or limits.","Tune driver timeouts for remote connections.","Prevent system sleep during long-running batch jobs."],"tags":["database","sql","connection","streaming"],"backgroundTag":"rows-iteration-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}