RocketChat/Rocket.Chat · error · Error

AutoCloseOnHoldScheduler is not running

Error message

AutoCloseOnHoldScheduler is not running

What it means

AutoCloseOnHoldScheduler.scheduleRoom throws if the service's running flag is false: the agenda-backed scheduler was never started (its start() had not completed) or was already stopped. scheduleRoom arms the auto-close timer when an omnichannel room is put on hold.

Source

Thrown at apps/meteor/ee/server/lib/omnichannel/AutoCloseOnHoldScheduler.ts:46

		if (this.running) {
			return;
		}

		this.scheduler = new Agenda({
			mongo: (MongoInternals.defaultRemoteCollectionDriver().mongo as any).client.db(),
			db: { collection: SCHEDULER_NAME },
			defaultConcurrency: 1,
			processEvery: process.env.TEST_MODE === 'true' || process.env.TEST_MODE === 'api' ? '3 seconds' : '1 minute',
		});

		await this.scheduler.start();
		this.running = true;
		this.logger.info('Service started');
	}

	public async scheduleRoom(roomId: string, timeout: number, comment: string): Promise<void> {
		if (!this.running) {
			throw new Error('AutoCloseOnHoldScheduler is not running');
		}

		this.logger.debug({ msg: 'Scheduling room to be closed', roomId, timeoutSeconds: timeout });
		await this.unscheduleRoom(roomId);

		const jobName = `${SCHEDULER_NAME}-${roomId}`;
		const when = moment(new Date()).add(timeout, 's').toDate();

		this.scheduler.define(jobName, this.executeJob.bind(this));
		await this.scheduler.schedule(when, jobName, { roomId, comment });
	}

	public async unscheduleRoom(roomId: string): Promise<void> {
		if (!this.running) {
			throw new Error('AutoCloseOnHoldScheduler is not running');
		}
		this.logger.debug({ msg: 'Unscheduling room', roomId });
		const jobName = `${SCHEDULER_NAME}-${roomId}`;

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Confirm the service actually started: look for the 'Service started' log from AutoCloseOnHoldScheduler
  2. If start() failed, fix the underlying agenda/Mongo connection issue and restart the server
  3. Guard early code paths with the scheduler's running flag and re-queue the operation once started

Example fix

// before
await scheduler.scheduleRoom(roomId, timeout, comment); // throws if not running

// after
if (!schedulerRunning()) {
	await startScheduler();
}
await scheduler.scheduleRoom(roomId, timeout, comment);
Defensive patterns

Strategy: validation

Validate before calling

if (!scheduler.isRunning?.()) {
	await scheduler.start();
}
await scheduler.scheduleRoom(roomId, timeout, comment);

Try / catch

try {
	await scheduler.scheduleRoom(roomId, timeout, comment);
} catch (err) {
	if (err instanceof Error && err.message === 'AutoCloseOnHoldScheduler is not running') {
		// engine not ready: re-queue after start completes; do not drop the room event
	} else {
		throw err;
	}
}

Prevention

When it happens

Trigger: Putting an omnichannel room on hold (which schedules auto-close) before AutoCloseOnHoldScheduler.start() completed during server startup, or after the service stopped; also in tests that use the scheduler without starting it.

Common situations: Livechat traffic arriving during startup; an earlier silent start() failure (Mongo/agenda connectivity); TEST_MODE misconfigured so startup ordering differs.

Related errors


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