gastownhall/beads · error

checking idx_wisps_is_blocked index

Error message

checking idx_wisps_is_blocked index: %w

What it means

This wraps a failure of schemaIndexExists while checking whether `idx_wisps_is_blocked` already exists on wisps. The repair mirrors ignored migration 0006's CREATE INDEX and must inspect first to remain idempotent. The error means the index-existence query itself failed, not that the index is missing.

Solutions

  1. Fix the underlying cause from the wrapped error (connectivity or privileges)
  2. Grant metadata-read access (SHOW INDEX / information_schema.STATISTICS)
  3. Re-run the repair; it re-verifies each step and is safe to retry
Defensive patterns

Strategy: try-catch

Validate before calling

var n int
if err := db.QueryRowContext(ctx,
    "SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = 'wisps' AND index_name = 'idx_wisps_is_blocked'").Scan(&n); err != nil {
    return fmt.Errorf("cannot inspect wisps indexes: %w", err)
}

Try / catch

err := ensureWispIsBlockedForRecompute(ctx, db)
if err != nil && strings.Contains(err.Error(), "checking idx_wisps_is_blocked index") {
    db = reconnect(db)
    return ensureWispIsBlockedForRecompute(ctx, db)
}

Prevention

When it happens

Trigger: ensureWispIsBlockedForRecompute got past the table/column checks, then ensureWispIsBlockedIndex's schemaIndexExists query errors: dropped connection, missing STATISTICS/metadata privileges, or a driver-level failure enumerating indexes on wisps.

Common situations: Privilege-restricted deployment users that can ALTER but not read index metadata; long repair sequences spanning a connection idle timeout; database failover between the column check and the index check.

Related errors


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

Appendix: source

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

	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
}

// wispsTableDDLForMigration0047 is 0020_create_wisps.up.sql's shape plus every
// column a main migration <=52 subsequently adds to wisps: no_history (0023)
// and started_at (0027). wisps is dolt_ignore'd (never replicated), so
// creating it here when it is missing cannot fork any synced clone: the
// table itself is always clone-local by design, and the ignored sequence's
// own guarded create (ignored/0001: CREATE a __temp__wisps, then RENAME it
// to wisps only if wisps does not already exist, else DROP the temp table)
// simply takes the DROP branch once this has run.

View on GitHub (pinned to 71377f2769)