gastownhall/beads · error

search %s (id scan): scan: %w

Error message

search %s (id scan): scan: %w

What it means

Raised when rows.Scan fails while reading a single id string column during scanFilterIDs. Because the query selects exactly one column into one string, failures usually mean a NULL id (schema corruption) or a driver conversion problem rather than column-count mismatch.

Source

Thrown at internal/storage/domain/db/issue_search.go:312

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)
}

func (r *issueSQLRepositoryImpl) hydrateIssues(ctx context.Context, issues []*types.Issue, tables filterTables, includeDeps bool, skipLabels bool) error {
	if len(issues) == 0 {
		return nil
	}

	ids := make([]string, len(issues))
	for i, issue := range issues {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Find the row with the NULL/malformed id: SELECT * FROM issues WHERE id IS NULL.
  2. Repair or delete the corrupt row(s); restore from Dolt history if writes were conflicting.
  3. Run `bd doctor` for integrity validation after repair.
  4. Restore the database from backup if corruption is widespread.

Example fix

// before
// issues table contains a row with NULL id -> scan into string fails
// after
DELETE FROM issues WHERE id IS NULL; -- or restore from dolt history
Defensive patterns

Strategy: validation

Validate before calling

// integrity check for NULL primary ids before search-heavy workflows
rows := query("SELECT COUNT(*) FROM issues WHERE id IS NULL OR id = ''")
if count > 0 { run bd doctor / restore from dolt history }

Type guard

func isIDScanTypeError(err error) bool {
	return err != nil && strings.Contains(err.Error(), "(id scan): scan: ")
}

Try / catch

page, err := store.Search(ctx, q, filter)
if err != nil && isIDScanTypeError(err) {
	return fmt.Errorf("corrupt issue rows (NULL id); restore from history or `bd doctor`: %w", err)
}

Prevention

When it happens

Trigger: The id-only query returns a row whose id column is NULL or non-text — typically from external edits, partial writes, or replication conflicts in Dolt that left a row without a primary id value.

Common situations: Manual SQL edits to the .beads database; a failed/crashed write leaving a partially inserted row; Dolt merge conflicts introducing NULL primary keys.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/95b7cf0ca7c306c6. Report an issue: GitHub.