gastownhall/beads · critical

adding wisps.is_blocked: %w

Error message

adding wisps.is_blocked: %w

What it means

This wraps failure of `ALTER TABLE wisps ADD COLUMN is_blocked TINYINT(1) NOT NULL DEFAULT 0`, executed when the repair confirmed the column is missing. Unlike the check errors, this is a real DDL failure — the schema was about to be changed and the statement was rejected or aborted. On MySQL this commonly fails due to permissions, a duplicate-column race, table lock contention, or insufficient disk during a table rebuild.

Source

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

	}
	if err := ensureWispIsBlockedColumn(ctx, db); err != nil {
		return err
	}
	return ensureWispIsBlockedIndex(ctx, db)
}

// ensureWispIsBlockedColumn is ignored/0006's ADD COLUMN statement,
// translated to Go for a clone that reached this repair without it.
func ensureWispIsBlockedColumn(ctx context.Context, db DBConn) error {
	hasColumn, err := schemaColumnExists(ctx, db, "wisps", "is_blocked")
	if err != nil {
		return fmt.Errorf("checking wisps.is_blocked column: %w", err)
	}
	if hasColumn {
		return nil
	}
	if _, err := db.ExecContext(ctx, "ALTER TABLE wisps ADD COLUMN is_blocked TINYINT(1) NOT NULL DEFAULT 0"); err != nil {
		return fmt.Errorf("adding wisps.is_blocked: %w", err)
	}
	return nil
}

// ensureWispIsBlockedIndex is ignored/0006's CREATE INDEX statement,
// translated to Go alongside ensureWispIsBlockedColumn above.
func ensureWispIsBlockedIndex(ctx context.Context, db DBConn) error {
	hasIndex, err := schemaIndexExists(ctx, db, "wisps", "idx_wisps_is_blocked")
	if err != nil {
		return fmt.Errorf("checking idx_wisps_is_blocked index: %w", err)
	}
	if hasIndex {
		return nil
	}
	if _, err := db.ExecContext(ctx, "CREATE INDEX idx_wisps_is_blocked ON wisps(is_blocked, status)"); err != nil {
		return fmt.Errorf("creating idx_wisps_is_blocked: %w", err)
	}
	return nil

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped driver error: ER_DUP_FIELDNAME means another process already added the column — re-run the repair and it will no-op
  2. Grant ALTER privilege to the migration user
  3. Ensure only one repair runs at a time (lock or serialize repair runs)
  4. For lock-wait timeouts, run the DDL during a quiet window or upgrade to a MySQL/Dolt version with online DDL
  5. Free disk space if the error indicates the table rebuild ran out of room

Example fix

// before: two workers racing the same repair
go runRepair(db); go runRepair(db)
// after: serialize repairs
mu.Lock(); defer mu.Unlock()
if err := runRepair(db); err != nil { return err }
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure no concurrent repair and sufficient privileges before ALTER
var privOK int
_ = db.QueryRowContext(ctx,
    "SELECT COUNT(*) FROM information_schema.user_privileges WHERE grantee = CURRENT_USER() AND privilege_type IN ('ALTER','CREATE')").Scan(&privOK)
if privOK == 0 {
    return errors.New("repair user lacks ALTER privilege; aborting before DDL")
}

Try / catch

if err := ensureWispIsBlockedForRecompute(ctx, db); err != nil {
    var drv *mysql.MySQLError
    if errors.As(err, &drv) {
        switch drv.Number {
        case 1060: // ER_DUP_FIELDNAME — column added concurrently, safe to re-run
            return ensureWispIsBlockedForRecompute(ctx, db)
        case 1205: // lock wait timeout
            return fmt.Errorf("DDL blocked; retry in a quiet window: %w", err)
        }
    }
    return err
}

Prevention

When it happens

Trigger: ensureWispIsBlockedColumn ran on a clone whose wisps table exists without is_blocked, and the ExecContext ALTER TABLE fails: missing ALTER privilege, another process concurrently adding the same column, metadata/DDL lock timeout, or disk-full during the implicit table copy.

Common situations: Two instances of the tool repairing the same database simultaneously; running repairs as a read-mostly user; large wisps tables making the DDL slow and prone to lock-wait timeouts on MySQL 5.x without online DDL.

Related errors


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