gastownhall/beads · error

remote-migrate gate: read pending versions: %w

Error message

remote-migrate gate: read pending versions: %w

What it means

Wraps a failure from PendingVersions while the remote-migrate gate lists schema migrations not yet applied. PendingVersions returning an empty list means the DB is current and migration is skipped; this error means the query to compute that list failed. Thrown before any fork-risk checks, so no migration is attempted.

Source

Thrown at internal/storage/schema/remote_migrate_gate.go:412

// both a configured sync remote and the on-disk remote-check fallback.
func CheckRemoteMigrateGateForRemoteWithRemoteCheckAndAdopt(ctx context.Context, db DBConn, remoteName string, extraHasRemote func() bool, adopt *FastForwardAdopter) error {
	return checkRemoteMigrateGate(ctx, db, remoteName, extraHasRemote, adopt)
}

func checkRemoteMigrateGate(ctx context.Context, db DBConn, remoteName string, extraHasRemote func() bool, adopt *FastForwardAdopter) error {
	// CurrentVersion treats a missing schema_migrations table as version 0, so a
	// brand-new database falls through the current==0 check below — nothing to fork.
	current, err := CurrentVersion(ctx, db)
	if err != nil {
		return fmt.Errorf("remote-migrate gate: read current version: %w", err)
	}
	if current == 0 {
		return nil // fresh database — nothing to fork
	}

	pending, err := PendingVersions(ctx, db)
	if err != nil {
		return fmt.Errorf("remote-migrate gate: read pending versions: %w", err)
	}
	if len(pending) == 0 {
		return nil // already current — nothing to migrate
	}

	hasRemote, err := anyDoltRemoteConfigured(ctx, db)
	if err != nil {
		return fmt.Errorf("remote-migrate gate: read remotes: %w", err)
	}
	// dolt_remotes can read empty even when a remote is configured: a freshly
	// (auto-)started server has not yet synced CLI remotes from .dolt/config
	// (GH#2315). Consult the caller's on-disk probe before allowing migration.
	if !hasRemote && extraHasRemote != nil {
		hasRemote = extraHasRemote()
	}
	if !hasRemote {
		return nil // no remote — no cross-clone fork risk
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped driver error and restore DB connectivity.
  2. If another clone is actively migrating, wait for it to finish and `bd dolt pull` before reopening.
  3. Verify schema_migrations is intact; repair from Dolt history if a partial write corrupted it.
  4. Retry the store open — the gate is read-only up to this point and safe to re-run.
Defensive patterns

Strategy: retry

Validate before calling

// verify the migrations table is readable before the gate runs
var n int
err := db.QueryRowContext(ctx, `SELECT COUNT(*) FROM schema_migrations`).Scan(&n)

Type guard

func isPendingReadErr(err error) bool {
    return strings.Contains(err.Error(), "remote-migrate gate: read pending versions")
}

Try / catch

if err := CheckRemoteMigrateGate(ctx, db, remote, extraHasRemote); err != nil {
    if isPendingReadErr(err) && isTransientDB(err) {
        time.Sleep(backoff)
        return CheckRemoteMigrateGate(ctx, db, remote, extraHasRemote)
    }
    return err
}

Prevention

When it happens

Trigger: PendingVersions(ctx, db) errors in checkRemoteMigrateGate — server unreachable, schema_migrations unreadable/inconsistent, context cancelled during store open.

Common situations: Startup auto-migration (`autoMigrateOnVersionBump`) against a remote-backed DB whose server was restarted or whose migrations table is mid-write from another clone pushing; network flakiness; insufficient privileges.

Related errors


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