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
- Run pending migrations so the schema matches the scanner's expectations
- Check which column fails to scan in the wrapped error and inspect that row's data
- Rebuild/reindex corrupted rows or restore from backup if data is malformed
- 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
- Never skip migrations when upgrading; scan errors are schema-drift symptoms
- Add a startup schema-version check against the binary's expectations
- Avoid hand-editing issue rows directly; NULLs break scanners
- Run integrity checks (bd doctor / DB check) after crashes
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
- db: ChildCounterSQLRepository.NextChildID: scan: %w
- db: CommentSQLRepository.CountsByIssueIDs: scan: %w
- db: CommentSQLRepository.ListByIssueIDs: scan: %w
- db: LabelSQLRepository.List: scan: %w
- db: LabelSQLRepository.ListByIssueIDs: scan: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/ff25144b0b5ef4bd.
Report an issue: GitHub.