{"record":{"id":"db98774721221102","repo":"gastownhall/beads","slug":"search-s-hydrate-w-db9877","errorCode":null,"errorMessage":"search %s: hydrate: %w","messagePattern":"search (.+?): hydrate: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/issue_search.go","lineNumber":289,"sourceCode":"\t\tif seen[issue.ID] {\n\t\t\tcontinue\n\t\t}\n\t\tseen[issue.ID] = true\n\t\tissues = append(issues, issue)\n\t}\n\t_ = rows.Close()\n\tif err := rows.Err(); err != nil {\n\t\treturn domain.SearchPage{}, fmt.Errorf(\"search %s: rows: %w\", tables.Main, err)\n\t}\n\n\tsortRowsGoSide(issues, func(i *types.Issue) string { return i.ID }, filter.SortBy, filter.SortDesc)\n\titems, hasMore, err := finishWindow(issues, window)\n\tif err != nil {\n\t\treturn domain.SearchPage{}, err\n\t}\n\n\tif err := r.hydrateIssues(ctx, items, tables, filter.IncludeDependencies, filter.SkipLabels); err != nil {\n\t\treturn domain.SearchPage{}, fmt.Errorf(\"search %s: hydrate: %w\", tables.Main, err)\n\t}\n\n\treturn domain.SearchPage{Items: items, HasMore: hasMore}, nil\n}\n\nfunc (r *issueSQLRepositoryImpl) scanFilterIDs(ctx context.Context, selectKw, fromSQL, whereSQL string, args []any, filter types.IssueFilter, tables filterTables) ([]string, bool, error) {\n\torderBy := orderBySQL(filter.SortBy, filter.SortDesc, tables.Main)\n\twindow := searchWindowForFilter(filter)\n\t//nolint:gosec // G201: SQL fragments from fixed table names and parameterized filters.\n\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","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/issue_search.go#L271-L307","documentation":"Raised when hydrateIssues fails after the core search rows were fetched successfully. Hydration fetches labels (and optionally dependency records) for the page of issues from their side tables; any error there is wrapped as \"search <table>: hydrate: <err>\". The search itself worked, but assembling full Issue objects failed.","triggerScenarios":"searchTable succeeds on the main query but getLabelsFromTable or getDependencyRecordsFromTable fails for the hydrated IDs: labels/dependencies/wisp_labels table missing, locked DB, or a mid-hydration connection loss.","commonSituations":"Older database missing the labels or dependencies table; corrupt side tables after a manual edit; concurrent bd process holding a write lock so secondary reads stall/timeout; ephemeral wisp searches against a DB missing optional wisp side tables (though those are usually tolerated as missing-optional).","solutions":["Unwrap to see which sub-fetch (labels vs dependencies) failed and why.","Run `bd doctor` and migrations to ensure labels/dependencies tables exist and match the schema.","Clear DB locks by stopping other bd processes, then retry the search.","If a wisp side table is legitimately absent, set SkipWisps / Ephemeral filters to search the issues plane only."],"exampleFix":"// before\nfilter := types.IssueFilter{} // hits wisps plane, missing wisp_labels table\n// after\nfilter := types.IssueFilter{SkipWisps: true} // search issues plane only","handlingStrategy":"validation","validationCode":"// confirm side tables exist before dependency-inclusive or wisp searches\nif err := bd.CheckTables(dbPath, \"issues\", \"labels\", \"dependencies\"); err != nil {\n\treturn fmt.Errorf(\"run `bd migrate`: %w\", err)\n}","typeGuard":"func isHydrateError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \": hydrate: \")\n}","tryCatchPattern":"page, err := store.Search(ctx, q, filter)\nif err != nil && isHydrateError(err) {\n\tif name, ok := dberrors.MissingTableName(err); ok {\n\t\t// fall back to a plain search without hydration-dependent filters\n\t\tfilter.IncludeDependencies = false\n\t\tfilter.SkipWisps = true\n\t\tpage, err = store.Search(ctx, q, filter)\n\t}\n}","preventionTips":["Run migrations whenever bd adds new side tables.","Set SkipWisps when working with databases that intentionally lack wisp tables.","Avoid concurrent writers that lock side tables during searches.","Validate label/dependency rows after external edits."],"tags":["database","hydration","labels","dependencies"],"backgroundTag":"hydration-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}