gastownhall/beads · error
partition wisp ids: scan: %w
Error message
partition wisp ids: scan: %w
What it means
During PartitionWispIDsInTx row iteration, rows.Scan into a single string ID failed. This wraps the sql.Rows.Scan error — typically a type/conversion mismatch or a driver-level row error surfaced at scan time. The partial wispSet is discarded so callers never see an untrustworthy partition.
Source
Thrown at internal/storage/issueops/wisp_routing.go:170
placeholders[i] = "?"
args[i] = id
}
//nolint:gosec // G201: only ? placeholders in the IN clause.
rows, qErr := tx.QueryContext(ctx,
fmt.Sprintf("SELECT id FROM wisps WHERE id IN (%s)", strings.Join(placeholders, ",")),
args...)
if qErr != nil {
// Wisps table may not exist yet on older schemas — treat as "no wisps".
if isTableNotExistError(qErr) {
return nil, append([]string(nil), ids...), nil
}
return nil, nil, fmt.Errorf("partition wisp ids: %w", qErr)
}
for rows.Next() {
var id string
if scanErr := rows.Scan(&id); scanErr != nil {
_ = rows.Close()
return nil, nil, fmt.Errorf("partition wisp ids: scan: %w", scanErr)
}
wispSet[id] = struct{}{}
}
_ = rows.Close()
if rowsErr := rows.Err(); rowsErr != nil {
return nil, nil, fmt.Errorf("partition wisp ids: rows: %w", rowsErr)
}
}
for _, id := range ids {
if _, ok := wispSet[id]; ok {
wispIDs = append(wispIDs, id)
} else {
permIDs = append(permIDs, id)
}
}
return wispIDs, permIDs, nil
}View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped scan error for sql.ErrNoRows vs conversion errors; verify the wisps.id column type matches expectations
- Re-run migrations so the schema matches what this code expects (id as string)
- Retry with a fresh connection/transaction if the cause was a dropped connection
- Ensure the context is not cancelled prematurely by the caller (check parent deadlines)
Example fix
// before rows, err := tx.QueryContext(ctx, q, args...) // ... Scan fails with conversion error after schema drift // after // 1. run migrations: bd migrate (or equivalent) to restore wisps.id type // 2. retry the partition on a fresh tx wispIDs, permIDs, err := issueops.PartitionWispIDsInTx(freshCtx, freshTx, ids)
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure schema matches expectations before partitioning _ = store.RunMigrations(ctx) // keeps wisps.id column type as expected
Try / catch
wispIDs, permIDs, err := issueops.PartitionWispIDsInTx(ctx, tx, ids)
if err != nil {
var scanErr *fmt.ScanError // or inspect wrapped driver error
if strings.Contains(err.Error(), "scan") {
// schema drift suspected: run migrations, then retry
}
return err
} Prevention
- Pin schema version and run migrations before app start
- Avoid altering wisps.id column type without a migration plan
- Ensure parent contexts outlive the row iteration
- Use stable connections (no aggressive proxy idle kills) for long result streams
When it happens
Trigger: Calling PartitionWispIDsInTx (or its wrappers: GetCommentsForIssuesInTx, GetCommentCountsInTx, ResolveDeletionSetInTx, GetDependencyRecordsForIssuesInTx, GetBlockingInfoForIssuesInTx, GetLabelsForIssuesInTx) when a row in the wisps result set cannot be scanned into a string — e.g. the id column type changed, the connection broke mid-iteration, or the context was cancelled between Next() and Scan().
Common situations: Schema drift where wisps.id is no longer a plain string/varchar; connection killed mid-result-stream by a proxy or Dolt server restart; context cancellation racing row iteration.
Related errors
- wisp id set: scan: %w
- get dependency records: scan: %w
- wake expired defers: scan %s row: %w
- wisp id set: probe: %w
- wisp id set: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/73800b42cc71bb4a.
Report an issue: GitHub.