gastownhall/beads · error

iterating dependencies rows for migration 0053 id backfill:

Error message

iterating dependencies rows for migration 0053 id backfill: %w

What it means

This wraps a rows.Err() failure after iterating the backfill SELECT over dependencies rows with id IS NULL. rows.Err() reports a driver/network error encountered mid-iteration (e.g. connection dropped while streaming rows), distinct from a scan-type mismatch. All rows read so far are discarded and the repair aborts before updating any id.

Source

Thrown at internal/storage/schema/migration_repairs.go:476

	`)
	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.
			continue
		}
		id := depid.New(e.issueID, target)
		if _, err := db.ExecContext(ctx, `
			UPDATE dependencies SET id = ?
			WHERE issue_id = ?

View on GitHub (pinned to 71377f2769)

Solutions

  1. Re-run the repair — the WHERE id IS NULL filter makes backfill resumable and idempotent
  2. Increase connection idle/wait timeouts in the DSN or server config
  3. For large tables, chunk the backfill (LIMIT/OFFSET or by key) to shorten result-set lifetime
  4. Stabilize the network path or run the repair locally against the clone

Example fix

// before: one giant streaming read over a flaky link
rows := query("... WHERE id IS NULL")
// after: chunked, resumable backfill
for {
    rows := query("... WHERE id IS NULL LIMIT 1000")
    if n := backfillChunk(rows); n == 0 { break }
}
Defensive patterns

Strategy: retry

Validate before calling

// ensure a stable connection before a potentially long streaming backfill
if err := db.PingContext(ctx); err != nil {
    return fmt.Errorf("repair aborted, unstable connection: %w", err)
}

Try / catch

err := repairV53RigAndSplitTargets(ctx, db)
if err != nil && strings.Contains(err.Error(), "iterating dependencies rows") {
    // transient stream failure — backfill is resumable via WHERE id IS NULL
    db = reconnect(db)
    return repairV53RigAndSplitTargets(ctx, db)
}

Prevention

When it happens

Trigger: backfillDependenciesID collected some edges, then rows.Err() returns non-nil because the underlying connection failed or the server aborted the result set during iteration — idle timeouts, max_allowed_packet issues on wide result sets, or server shutdown mid-query.

Common situations: Long backfills over a flaky VPN/proxy connection; MySQL wait_timeout killing an idle-then-streaming connection; server memory/limit kills on very large dependencies tables.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/61ca8a58bfdff753. Report an issue: GitHub.