gastownhall/beads · error
search %s (id scan): %w
Error message
search %s (id scan): %w
What it means
Raised when the ID-scan phase of searchTable (scanFilterIDs) fails to execute its query. This lighter query selects only table.id with the same FROM/WHERE/ORDER/window clauses; execution errors from the driver are wrapped as "search <table> (id scan): <err>". The ID-scan path is used for a subset of filters, so this error appears only on those code paths.
Source
Thrown at internal/storage/domain/db/issue_search.go:304
}
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)
//nolint:gosec // G201: SQL fragments from fixed table names and parameterized filters.
idQuery := fmt.Sprintf(`%s%s.id FROM %s %s %s %s`,
selectKw, tables.Main, fromSQL, whereSQL, orderBy, window.sql)
rows, err := r.runner.QueryContext(ctx, idQuery, args...)
if err != nil {
return nil, false, fmt.Errorf("search %s (id scan): %w", tables.Main, err)
}
defer func() { _ = rows.Close() }()
var ids []string
for rows.Next() {
var id string
if err := rows.Scan(&id); err != nil {
return nil, false, fmt.Errorf("search %s (id scan): scan: %w", tables.Main, err)
}
ids = append(ids, id)
}
if err := rows.Err(); err != nil {
return nil, false, fmt.Errorf("search %s (id scan): rows: %w", tables.Main, err)
}
sortRowsGoSide(ids, func(id string) string { return id }, filter.SortBy, filter.SortDesc)
return finishWindow(ids, window)
}View on GitHub (pinned to 71377f2769)
Solutions
- Unwrap the error chain to identify the driver-level cause (no such table, locked, connection).
- Run `bd doctor` to verify database integrity and schema; re-migrate if needed.
- Release concurrent locks (close other bd sessions) and retry.
- Extend the context deadline or add filters to shrink the scanned table.
Example fix
// before ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) // too short for id scan // after ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
Defensive patterns
Strategy: try-catch
Validate before calling
if _, err := os.Stat(dbPath); err != nil { return err }
if err := ctx.Err(); err != nil { return err } Type guard
func isIDScanError(err error) bool {
return err != nil && strings.Contains(err.Error(), "(id scan)")
} Try / catch
page, err := store.Search(ctx, q, filter)
if err != nil && isIDScanError(err) {
if dberrors.IsMissingTable(err) {
return fmt.Errorf("database out of date, run `bd migrate`: %w", err)
}
// locked or transient: retry once
} Prevention
- Keep schema migrations current with the bd binary version.
- Use realistic context timeouts for filtered scans of large tables.
- Serialize write access to the database across processes.
When it happens
Trigger: Calling a search path that routes through scanFilterIDs when the main query against the issues/wisps table fails: missing table, locked DB, connection failure, or cancelled context — same class as the full-search query failure but from the id-only SQL.
Common situations: Database schema drift (table absent); DB file locked by another process; context deadline shorter than the query on large tables; remote Dolt server unreachable.
Related errors
- search %s: %w
- get dependents from %s: %w
- count edges in %s: %w
- check issue existence in %s: %w
- failed to begin transaction: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/b0398c91004d3a41.
Report an issue: GitHub.