gastownhall/beads · error
scan is_blocked from %s: %w
Error message
scan is_blocked from %s: %w
What it means
This error wraps a *sql.Rows.Scan failure while reading (issue_id, is_blocked) pairs in readIsBlockedIntoFromTable, part of IsBlockedBatchInTx. It fires when a row's column values cannot be converted into the declared Go destinations (a string id and an int blocked flag) — typically a column-count or type mismatch between the query and Scan targets. The library throws it so batch is_blocked computation fails loudly rather than silently producing wrong blocking state.
Source
Thrown at internal/storage/issueops/dependency_queries.go:1041
//nolint:gosec // G201: table is a hardcoded "issues" or "wisps"; placeholders are ? only.
func readIsBlockedIntoFromTable(ctx context.Context, tx DBTX, table string, ids []string, seen, blocked map[string]bool) error {
for start := 0; start < len(ids); start += queryBatchSize {
end := start + queryBatchSize
if end > len(ids) {
end = len(ids)
}
placeholders, args := buildSQLInClause(ids[start:end])
rows, err := tx.QueryContext(ctx, fmt.Sprintf(
"SELECT id, is_blocked FROM %s WHERE id IN (%s)", table, placeholders), args...)
if err != nil {
return fmt.Errorf("read is_blocked from %s: %w", table, err)
}
for rows.Next() {
var id string
var b int
if err := rows.Scan(&id, &b); err != nil {
_ = rows.Close()
return fmt.Errorf("scan is_blocked from %s: %w", table, err)
}
// Keep the first-seen (issues) value and skip any later (wisps)
// duplicate, so the batch is_blocked matches per-row IsBlocked.
if seen[id] {
continue
}
seen[id] = true
blocked[id] = b != 0
}
_ = rows.Close()
if err := rows.Err(); err != nil {
return fmt.Errorf("is_blocked rows from %s: %w", table, err)
}
}
return nil
}
// scanDependencyRow scans a single dependency row from a *sql.Rows.View on GitHub (pinned to 71377f2769)
Solutions
- Run schema migrations / `bd doctor` to align the database schema with the binary's expected tables and columns.
- Rebuild or re-sync the database from the canonical source (bd dolt pull / re-import) if schema drift is unfixable in place.
- Upgrade or downgrade the beads binary so its query matches the existing schema version.
- Check driver compatibility (dolthub driver version) if the type of the computed is_blocked column changed.
Example fix
// before: schema drifted, extra column returned by query
if err := rows.Scan(&id, &b); err != nil {
return fmt.Errorf("scan is_blocked from %s: %w", table, err)
}
// after: make destinations match query shape, tolerate NULL
var bid sql.NullInt64
if err := rows.Scan(&id, &bid); err != nil {
return fmt.Errorf("scan is_blocked from %s: %w", table, err)
}
b = int(bid.Int64) Defensive patterns
Strategy: try-catch
Validate before calling
// verify schema before batch check
rows, err := db.Query("SELECT issue_id, is_blocked FROM issues LIMIT 1")
if err != nil { return err }
cols, _ := rows.Columns()
if len(cols) != 2 || cols[0] != "issue_id" || cols[1] != "is_blocked" {
return fmt.Errorf("unexpected issues shape %v; run migrations", cols)
}
rows.Close() Type guard
func isScanErr(err error) bool {
var se *sql.StorageError
return errors.As(err, &se) || strings.Contains(err.Error(), "sql: expected")
} Try / catch
blocked, err := ops.IsBlockedBatchInTx(ctx, tx, ids)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) || isTransient(err) {
return retryBatch(ctx, ids)
}
return fmt.Errorf("batch is_blocked unavailable (schema drift?): %w", err)
} Prevention
- Always run schema migrations before pointing a beads binary at an existing database.
- Use `bd doctor` as a preflight in CI to detect schema drift early.
- Pin driver and beads versions together; don't mix schema generations.
- Avoid manual ALTER TABLE on tables beads owns.
When it happens
Trigger: Calling IsBlockedBatchInTx (directly or via higher-level batch blocking APIs) when the underlying issues/wisps table schema no longer matches the hardcoded SELECT column list — e.g. the query returns extra/missing columns or the is_blocked expression changes type (e.g. NULL, string instead of int).
Common situations: Running a beads binary against a Dolt database created by a different (older/newer) version whose dependency/is_blocked schema drifted; manual schema edits; a driver that returns a different integer type for the boolean expression.
Related errors
- scan dependency: %w
- count inbound dependencies from %s: %w
- scan inbound dependency: %w
- retarget inbound dependencies to wisp in %s for %s: %w
- retarget inbound dependencies to issue in %s for %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/d44aef9f32b20dc7.
Report an issue: GitHub.