gastownhall/beads · error
scan comment: %w
Error message
scan comment: %w
What it means
After querying comments, each row is scanned into a types.Comment via rows.Scan; any column-count or type mismatch produces "scan comment: %w". It indicates the result row shape does not match the five expected columns (id, issue_id, author, text, created_at).
Source
Thrown at internal/storage/issueops/bulk_ops.go:159
placeholders, args := buildSQLInClause(batch)
query := fmt.Sprintf(`
SELECT id, issue_id, author, text, created_at
FROM %s
WHERE issue_id IN (%s)
ORDER BY issue_id, created_at ASC, id ASC
`, table, placeholders)
rows, err := tx.QueryContext(ctx, query, args...)
if err != nil {
return fmt.Errorf("get comments from %s: %w", table, err)
}
for rows.Next() {
var c types.Comment
if err := rows.Scan(&c.ID, &c.IssueID, &c.Author, &c.Text, &c.CreatedAt); err != nil {
_ = rows.Close()
return fmt.Errorf("scan comment: %w", err)
}
result[c.IssueID] = append(result[c.IssueID], &c)
}
if err := rows.Err(); err != nil {
_ = rows.Close()
return err
}
_ = rows.Close()
}
return nil
}
// DeleteIssuesBySourceRepoInTx removes all issues from a source repo and their related data.
//
//nolint:gosec // G201: table is validated by hardcoded list
func DeleteIssuesBySourceRepoInTx(ctx context.Context, tx *sql.Tx, sourceRepo string) (int, error) {
rows, err := tx.QueryContext(ctx, `SELECT id FROM issues WHERE source_repo = ?`, sourceRepo)
if err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Find the wrapped scan error to see which column failed (e.g. "converting NULL to string")
- Fix the offending row data (replace NULLs with defaults) or make the target field a pointer/nullable type
- Re-run migrations so the schema matches the SELECT column list
- If schema is intentionally different, update the SELECT and Scan targets together
Example fix
// before: NULL created_at panics the scan
var c types.Comment
rows.Scan(&c.ID, &c.IssueID, &c.Author, &c.Text, &c.CreatedAt)
// after: tolerate NULLs
var createdAt sql.NullTime
rows.Scan(&c.ID, &c.IssueID, &c.Author, &c.Text, &createdAt)
if createdAt.Valid { c.CreatedAt = createdAt.Time } Defensive patterns
Strategy: validation
Validate before calling
// sanity-check row data before bulk reads if you can query directly
rows, _ := db.Query("SELECT COUNT(*) FROM comments WHERE created_at IS NULL OR author IS NULL") Type guard
// narrow the scan failure
var convErr *sql.ConvertError
if errors.As(err, &convErr) { /* log column convErr.Source/Value */ } Try / catch
if err := GetCommentsForIssuesInTx(ctx, tx, ids); err != nil {
if strings.Contains(err.Error(), "converting NULL") {
// repair NULL columns or upgrade to a nullable schema
}
return err
} Prevention
- Avoid hand-editing database rows; keep NOT NULL columns populated
- Re-run migrations after any schema change or version upgrade
- Keep the SELECT column list and struct fields in sync when forking
When it happens
Trigger: rows.Scan returns an error because a column is NULL into a non-pointer non-nullable field (e.g. NULL author or created_at), the table schema diverged from the SELECT column list, or the driver returns an incompatible type for a scanned field.
Common situations: Hand-edited or partially migrated database where comments rows have NULL created_at; custom fork changed the comments schema but not this query; driver type mismatch after a storage backend change.
Related errors
- iter issues: scan: %w
- scan issue ID: %w
- get dependent records: scan: %w
- scan dependent: %w
- get dependency counts: scan blocker: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a4669d11de52e798.
Report an issue: GitHub.