gastownhall/beads · error
scanning dependencies row for migration 0053 id backfill: %w
Error message
scanning dependencies row for migration 0053 id backfill: %w
What it means
This wraps a rows.Scan failure while reading dependencies rows whose id is NULL for deterministic backfill during the migration-0053 repair. Scanning fails when the actual column types/nullability in the database do not match the scan destinations (string + three sql.NullString). It indicates schema drift or corrupted row data rather than a query failure.
Source
Thrown at internal/storage/schema/migration_repairs.go:471
func backfillDependenciesID(ctx context.Context, db DBConn) error {
rows, err := db.QueryContext(ctx, `
SELECT issue_id, depends_on_issue_id, depends_on_wisp_id, depends_on_external
FROM dependencies
WHERE id IS NULL
`)
if err != nil {
return fmt.Errorf("reading dependencies rows for migration 0053 id backfill: %w", err)
}
type edge struct {
issueID string
dependsOnIssueID, dependsOnWispID, dependsOnExternal sql.NullString
}
var edges []edge
for rows.Next() {
var e edge
if err := rows.Scan(&e.issueID, &e.dependsOnIssueID, &e.dependsOnWispID, &e.dependsOnExternal); err != nil {
_ = rows.Close()
return fmt.Errorf("scanning dependencies row for migration 0053 id backfill: %w", err)
}
edges = append(edges, e)
}
if err := rows.Err(); err != nil {
return fmt.Errorf("iterating dependencies rows for migration 0053 id backfill: %w", err)
}
_ = rows.Close()
for _, e := range edges {
target := firstNonNullString(e.dependsOnIssueID, e.dependsOnWispID, e.dependsOnExternal)
if target == "" {
// ck_dep_one_target (0041) should make a targetless row
// unreachable; if one exists anyway, leave its id NULL here --
// ensureDependenciesIDPrimaryKey below checks for exactly this
// and fails loudly with an actionable count instead of letting a
// blind MODIFY ... NOT NULL hard-fail on it, or silently keying
// the table while pretending the row doesn't exist.
continueView on GitHub (pinned to 71377f2769)
Solutions
- Find the offending row: run the same WHERE id IS NULL query manually and inspect NULLs/types
- Fix data: backfill or delete rows where issue_id IS NULL before re-running the repair
- Align column types with expectations (issue_id NOT NULL VARCHAR/CHAR)
- If the driver returns []byte, ensure DSN settings (e.g. interpolateParams/charset) or scan into sql.RawBytes/[]byte
Example fix
// before: string target rejects NULL issue_id
var e edge
rows.Scan(&e.issueID, ...)
// after: tolerate NULLs at the source
var issueID sql.NullString
rows.Scan(&issueID, &e.dependsOnIssueID, &e.dependsOnWispID, &e.dependsOnExternal)
if !issueID.Valid { skip or repair row } Defensive patterns
Strategy: validation
Validate before calling
// detect NULL or mistyped issue_id rows before running the repair
rows, err := db.QueryContext(ctx, `
SELECT COUNT(*) FROM dependencies
WHERE id IS NULL AND (issue_id IS NULL
OR depends_on_issue_id IS NULL AND depends_on_wisp_id IS NULL AND depends_on_external IS NULL)`)
if err != nil { return err }
var bad int
rows.Scan(&bad)
if bad > 0 {
return fmt.Errorf("%d dependencies rows need manual repair before id backfill", bad)
} Try / catch
if err := repairV53RigAndSplitTargets(ctx, db); err != nil {
if strings.Contains(err.Error(), "scanning dependencies row") {
// inspect offending rows: NULL issue_id or type drift
return fmt.Errorf("fix dependencies data (NULL issue_id / wrong column types) then retry: %w", err)
}
return err
} Prevention
- Enforce NOT NULL on dependencies.issue_id in schema
- Scan nullable columns into sql.NullString everywhere in repair code
- Audit rows with id IS NULL before each repair run
- Avoid hand-editing migration-applied tables
When it happens
Trigger: backfillDependenciesID executes `SELECT issue_id, depends_on_issue_id, depends_on_wisp_id, depends_on_external FROM dependencies WHERE id IS NULL` and rows.Scan errors on a row — e.g. issue_id NULL when the scan target is a plain string, or a driver type mismatch (bytes vs string) from an unexpected column type.
Common situations: A partially-applied or hand-edited migration left issue_id nullable with NULL rows; a driver/charset change returning []byte where string is expected; rows inserted by an older schema version with different types.
Related errors
- reading dependencies rows for migration 0053 id backfill: %w
- iterating dependencies rows for migration 0053 id backfill:
- schema: read database name: %w
- schema migration: %w
- checking wisps.is_blocked column: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/828a6563a8ef56c0.
Report an issue: GitHub.