gastownhall/beads · critical

backfilling wisp_dependencies split targets for migration 00

Error message

backfilling wisp_dependencies split targets for migration 0053: %w

What it means

Wraps a failed backfill UPDATE while copying legacy depends_on_id values into the split-target columns (depends_on_external / depends_on_wisp_id / depends_on_issue_id) as part of migration 0053 repair. This is critical to fix before migration 0005 runs: 0005 drops depends_on_id, after which the source data for an interrupted backfill is gone forever. The statements are idempotent (each updates only rows not yet filled), so a retry is safe.

Source

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

				return fmt.Errorf("adding wisp_dependencies.%s for migration 0053: %w", col.name, err)
			}
		}
	}

	legacyTarget, err := schemaColumnExists(ctx, db, "wisp_dependencies", "depends_on_id")
	if err != nil {
		return fmt.Errorf("checking wisp_dependencies.depends_on_id: %w", err)
	}
	if !legacyTarget {
		// Nothing left to backfill from: either a prior pass already
		// finished (depends_on_id has since been dropped) or this database
		// never had the legacy column to begin with.
		return nil
	}

	for _, repair := range wispDependenciesSplitTargetBackfillSQL() {
		if _, err := db.ExecContext(ctx, repair); err != nil {
			return fmt.Errorf("backfilling wisp_dependencies split targets for migration 0053: %w", err)
		}
	}
	return nil
}

func wispDependenciesSplitTargetColumns() []struct{ name, definition string } {
	return []struct{ name, definition string }{
		{"depends_on_issue_id", "VARCHAR(255) NULL"},
		{"depends_on_wisp_id", "VARCHAR(255) NULL"},
		{"depends_on_external", "VARCHAR(255) NULL"},
	}
}

func wispDependenciesSplitTargetBackfillSQL() []string {
	return []string{
		"UPDATE wisp_dependencies SET depends_on_external = depends_on_id WHERE depends_on_external IS NULL AND depends_on_id LIKE 'external:%'",
		"UPDATE wisp_dependencies wd JOIN wisps w ON w.id = wd.depends_on_id SET wd.depends_on_wisp_id = wd.depends_on_id WHERE wd.depends_on_wisp_id IS NULL AND wd.depends_on_external IS NULL",
		"UPDATE wisp_dependencies wd JOIN issues i ON i.id = wd.depends_on_id SET wd.depends_on_issue_id = wd.depends_on_id WHERE wd.depends_on_issue_id IS NULL AND wd.depends_on_external IS NULL AND wd.depends_on_wisp_id IS NULL",

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped driver error; if it is a lock/timeout issue, retry when the conflicting writer is done — the backfill is scoped and idempotent.
  2. DO NOT run or ignore migration 0005 until this backfill succeeds; depends_on_id is dropped by 0005 and the data would be unrecoverable.
  3. Verify referenced rows exist: for depends_on_id values, check matching rows in wisps and issues tables.
  4. Restore from Dolt history/backup if rows were corrupted, then re-run the repair before proceeding past 0005.
Defensive patterns

Strategy: validation

Validate before calling

// before letting migration 0005 run, verify the backfill completed
var unfilled int
db.QueryRowContext(ctx, `SELECT COUNT(*) FROM wisp_dependencies
  WHERE depends_on_id IS NOT NULL AND depends_on_external IS NULL
  AND depends_on_wisp_id IS NULL AND depends_on_issue_id IS NULL`).Scan(&unfilled)
// unfilled > 0 with depends_on_id still present means backfill incomplete

Type guard

func backfillComplete(rowsFilled, rowsTotal int) bool { return rowsFilled == rowsTotal }

Try / catch

if err := ensureWispDependenciesSplitTargets(ctx, db); err != nil {
    // DO NOT proceed to migration 0005; depends_on_id would be dropped
    return fmt.Errorf("refusing to continue toward migration 0005: %w", err)
}

Prevention

When it happens

Trigger: One of the four UPDATE statements in wispDependenciesSplitTargetBackfillSQL() fails — a JOIN to wisps/issues hits a corrupt/missing row, a constraint violation on the target column, lock contention, or server failure mid-statement.

Common situations: Interrupted first attempt left columns present but unpopulated and a retry now fails on data issues; wisps/issues tables missing rows referenced by depends_on_id; concurrent writers holding row locks on wisp_dependencies.

Related errors


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