gastownhall/beads · error

adding wisp_dependencies.%s for migration 0053: %w

Error message

adding wisp_dependencies.%s for migration 0053: %w

What it means

Wraps a failed ALTER TABLE wisp_dependencies ADD COLUMN when ensureWispDependenciesSplitTargets adds a missing split-target column (depends_on_issue_id/depends_on_wisp_id/depends_on_external) required by migration 0053. This is an actual DDL failure, so the schema may be partially repaired (some columns added, then one fails). The function is safe to re-run: present columns are skipped.

Source

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

// after that the source data needed to finish an interrupted backfill is
// gone for good.
func ensureWispDependenciesSplitTargets(ctx context.Context, db DBConn) error {
	table, err := schemaTableExists(ctx, db, "wisp_dependencies")
	if err != nil {
		return fmt.Errorf("checking wisp_dependencies table: %w", err)
	}
	if !table {
		return nil
	}

	for _, col := range wispDependenciesSplitTargetColumns() {
		present, err := schemaColumnExists(ctx, db, "wisp_dependencies", col.name)
		if err != nil {
			return fmt.Errorf("checking wisp_dependencies.%s: %w", col.name, err)
		}
		if !present {
			if _, err := db.ExecContext(ctx, "ALTER TABLE wisp_dependencies ADD COLUMN "+col.name+" "+col.definition); err != nil {
				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)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped driver error: if 'duplicate column', a concurrent repair already added it — simply re-run and it will skip present columns.
  2. Ensure only one process performs migrations at a time (use a lock or run `bd migrate` in a single session).
  3. Confirm the database is writable (not opened read-only) and the user has ALTER privilege.
  4. Re-run the repair; each step is idempotent and safe to repeat.
Defensive patterns

Strategy: try-catch

Validate before calling

// check column and skip ALTER if another process already added it
present, _ := schemaColumnExists(ctx, db, "wisp_dependencies", colName)
if present { /* skip ALTER */ }

Type guard

func isDuplicateColumnErr(err error) bool {
    return strings.Contains(err.Error(), "Duplicate column name")
}

Try / catch

err := ensureWispDependenciesSplitTargets(ctx, db)
if err != nil && isDuplicateColumnErr(err) {
    // concurrent repair won the race; safe to proceed
} else if err != nil {
    return err
}

Prevention

When it happens

Trigger: db.ExecContext("ALTER TABLE wisp_dependencies ADD COLUMN <name> <definition>") fails — duplicate column name raced in by a concurrent process, table locked by another writer, Dolt refusing DDL in the current session, or disk/resource errors.

Common situations: Two `bd` processes running migration repair concurrently on the same database; database opened read-only; Dolt version that rejects ALTER on a table with pending foreign keys; out-of-disk during DDL.

Related errors


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