RocketChat/Rocket.Chat · warning

Not migrating, control is locked. Will retry.

Error message

Not migrating, control is locked. Will retry.

What it means

Database migrations are guarded by a lock in the control document so only one instance migrates at a time. This warning means lock() failed — another instance holds the lock, or a crashed instance left control.locked=true. The server sleeps retryInterval (10s) and retries recursively, up to maxAttempts (30, ~5 minutes), after which it halts with 'ERROR! SERVER STOPPED — Database migration control is locked'.

Source

Thrown at apps/meteor/server/lib/migrations.ts:190

	}

	// version 0 means it is a fresh database, just set the control to latest known version and skip
	if (currentVersion === 0) {
		await setControl({
			locked: false,
			version: orderedMigrations[orderedMigrations.length - 1].version,
		});
		return true;
	}

	const version = targetVersion === 'latest' ? orderedMigrations[orderedMigrations.length - 1].version : targetVersion;

	// get latest version
	// const { version } = orderedMigrations[orderedMigrations.length - 1];

	if (!(await lock())) {
		if (currentAttempt <= maxAttempts) {
			log.warn({
				msg: 'Not migrating, control is locked. Will retry.',
				retryIntervalSeconds: retryInterval,
				attempt: currentAttempt,
				maxAttempts,
			});

			await sleep(retryInterval * 1000);

			currentAttempt++;
			return migrateDatabase(targetVersion, subcommands);
		}
		const control = await getControl(); // Side effect: upserts control document.
		showErrorBox(
			'ERROR! SERVER STOPPED',
			[
				'Your database migration control is locked.',
				'Please make sure you are running the latest version and try again.',
				'If the problem persists, please contact support.',

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Do nothing if another instance is legitimately migrating — this instance retries every 10s and proceeds once the lock releases
  2. If no other instance is running, clear the stale lock: in MongoDB set the migrations control document's locked=false (back up the database first)
  3. For rolling deployments, let the first instance finish booting before starting the rest
  4. If the server stops after ~30 attempts, inspect what holds the lock (another live instance? Mongo health?) and restart after resolving

Example fix

// mongo shell, only when no instance is migrating
// before
db.rocketchat_migrations.findOne() // { locked: true, version: 355, ... }

// after
db.rocketchat_migrations.updateOne({}, { $set: { locked: false } })
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight before starting an instance that will migrate
const control = db.collection('rocketchat_migrations').findOne();
if (control?.locked) {
  // find out who holds the lock; only clear it after confirming no instance is migrating
  logger.warn(`Migration lock held (version ${control.version}); waiting instead of forcing`);
}

Prevention

When it happens

Trigger: Booting multiple Rocket.Chat instances against the same database at a new version simultaneously (scale-up, k8s rollout, docker-compose scale); a previous migration that crashed mid-run leaving locked=true; an instance stuck mid-migration on a slow or unhealthy MongoDB.

Common situations: Rolling deployments where all replicas start at once; a server killed during migration (OOM, restart) so the lock was never released; dev and prod pointing at the same DB; replica set step-down during migration.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/186c8692026e6db4. Report an issue: GitHub.