{"record":{"id":"16a324e960ea4ea8","repo":"gastownhall/beads","slug":"search-s-scan-w","errorCode":null,"errorMessage":"search %s: scan: %w","messagePattern":"search (.+?): scan: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/issue_search.go","lineNumber":269,"sourceCode":"\torderBy := orderBySQL(filter.SortBy, filter.SortDesc, \"\")\n\twindow := searchWindowForFilter(filter)\n\n\t//nolint:gosec // G201: SQL fragments from fixed table names and parameterized filters.\n\tquerySQL := fmt.Sprintf(`%s%s FROM %s %s %s %s %s`,\n\t\tselectKw, issueSelectColumns, plan.FromSQL, sqlbuild.LeaseJoin(tables.Main), whereSQL, orderBy, window.sql)\n\n\trows, err := r.runner.QueryContext(ctx, querySQL, args...)\n\tif err != nil {\n\t\treturn domain.SearchPage{}, fmt.Errorf(\"search %s: %w\", tables.Main, err)\n\t}\n\n\tvar issues []*types.Issue\n\tseen := make(map[string]bool)\n\tfor rows.Next() {\n\t\tissue, scanErr := scanIssue(rows)\n\t\tif scanErr != nil {\n\t\t\t_ = rows.Close()\n\t\t\treturn domain.SearchPage{}, fmt.Errorf(\"search %s: scan: %w\", tables.Main, scanErr)\n\t\t}\n\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","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/issue_search.go#L251-L287","documentation":"Raised when rows.Scan (inside scanIssue) fails while iterating search result rows. Scan fails when a column's value cannot be converted into the destination Go type — e.g. NULL in a NOT-NULL-assumed column, a malformed value, or driver/column-count mismatch. The search wraps it as \"search <table>: scan: <err>\" and closes the rows before returning.","triggerScenarios":"A search hits rows whose stored data violates the expected column shape: NULL in a column scanIssue scans into a value type, text in an integer column, or a schema where issueSelectColumns count/order no longer matches the physical table (schema drift between bd versions).","commonSituations":"Hand-edited or externally modified Dolt/SQLite rows introducing NULLs; upgrading bd across a schema change without running migrations; partial migration leaving old columns in place.","solutions":["Read the wrapped scan error to find which column/value failed conversion.","Run `bd doctor` / schema migrations to bring the DB to the expected schema version.","Inspect the offending issue row and repair or delete corrupt rows.","Restore the database from a known-good backup (Dolt history or .beads backup)."],"exampleFix":"// before\n// rows contain NULL priority; scanIssue scans into *int with Scan(&priority) fine,\n// but a NULL into a plain int fails:\nissue, scanErr := scanIssue(rows) // \"sql: Scan error on column 'priority'\n// after\n// migrate/repair the row so values match the schema, or coalesce at read time:\nSELECT COALESCE(priority, 0) AS priority, ... FROM issues","handlingStrategy":"validation","validationCode":"// validate schema version before searching\nif err := bd.CheckSchemaVersion(dbPath); err != nil {\n\treturn fmt.Errorf(\"run `bd migrate` first: %w\", err)\n}","typeGuard":"func isScanError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \": scan: \")\n}","tryCatchPattern":"page, err := store.Search(ctx, q, filter)\nif err != nil && isScanError(err) {\n\t// schema drift or corrupt row; surface repair guidance to the user\n\treturn fmt.Errorf(\"%w (run `bd doctor` / `bd migrate`)\", err)\n}","preventionTips":["Always run migrations when upgrading bd versions; never skip versioned upgrades.","Do not hand-edit database rows with external SQL tools.","Back up .beads before bulk external modifications."],"tags":["database","sql","scan","schema-drift"],"backgroundTag":"sql-scan-type-mismatch","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}