gastownhall/beads · error

get issues by IDs: scan: %w

Error message

get issues by IDs: scan: %w

What it means

Wraps a row-scanning failure (ScanIssueFrom) while decoding an issue row from the ID-batch query in GetIssuesByIDsInTx. It means the query succeeded but a row's columns could not be mapped into types.Issue.

Source

Thrown at internal/storage/issueops/dependencies.go:1055

			args := make([]any, len(batch))
			for i, id := range batch {
				placeholders[i] = "?"
				args[i] = id
			}
			inClause := strings.Join(placeholders, ",")

			rows, err := tx.QueryContext(ctx, fmt.Sprintf(
				`SELECT %s FROM %s %s WHERE id IN (%s)`,
				IssueSelectColumns, pair.table, sqlbuild.LeaseJoin(pair.table), inClause), args...)
			if err != nil {
				return nil, fmt.Errorf("get issues by IDs from %s: %w", pair.table, err)
			}
			issueMap := make(map[string]*types.Issue)
			for rows.Next() {
				issue, scanErr := ScanIssueFrom(rows)
				if scanErr != nil {
					_ = rows.Close()
					return nil, fmt.Errorf("get issues by IDs: scan: %w", scanErr)
				}
				allIssues = append(allIssues, issue)
				issueMap[issue.ID] = issue
			}
			_ = rows.Close()
			if err := rows.Err(); err != nil {
				return nil, fmt.Errorf("get issues by IDs: rows: %w", err)
			}

			// Hydrate labels.
			if len(issueMap) > 0 {
				labelRows, err := tx.QueryContext(ctx, fmt.Sprintf(
					`SELECT issue_id, label FROM %s WHERE issue_id IN (%s) ORDER BY issue_id, label`,
					pair.labelTbl, inClause), args...)
				if err != nil {
					return nil, fmt.Errorf("get issues by IDs: labels from %s: %w", pair.labelTbl, err)
				}
				for labelRows.Next() {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Run pending migrations so the schema matches the scanner's expectations
  2. Check which column fails to scan in the wrapped error and inspect that row's data
  3. Rebuild/reindex corrupted rows or restore from backup if data is malformed
  4. Ensure the binary and schema are from the same version (no partial upgrade)
Defensive patterns

Strategy: validation

Validate before calling

// Before upgrading, verify schema matches the binary's expected columns:
// Compare IssueSelectColumns against the live table schema in a preflight check.
cols, err := db.QueryContext(ctx, `PRAGMA table_info(issues)`) // or driver equivalent
// Ensure no NOT-NULL-expected column is nullable in practice; fix data first.

Try / catch

issues, err := GetIssuesByIDsInTx(ctx, tx, ids, nil)
if err != nil && strings.Contains(err.Error(), "get issues by IDs: scan") {
    // Do NOT retry — scan errors are deterministic (schema/data drift).
    return fmt.Errorf("schema or data mismatch, run migrations/integrity check: %w", err)
}

Prevention

When it happens

Trigger: ScanIssueFrom fails on a row inside the `for rows.Next()` loop — column count/type mismatch between IssueSelectColumns and the struct, NULL in a non-nullable field, or corrupt data in the row.

Common situations: Schema drift: a migration added/removed/renamed a column but the scan code and DB are out of sync; manually edited rows with NULLs where the scan expects values; mixed table layouts between issues and wisps tables.

Related errors


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