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
- Confirm the service actually started: look for the 'Service started' log from AutoCloseOnHoldScheduler
- If start() failed, fix the underlying agenda/Mongo connection issue and restart the server
- 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
- Start the scheduler service before wiring livechat on-hold handlers
- Verify the 'Service started' log during startup checks
- In tests, always start the scheduler with short processEvery under TEST_MODE
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.