RocketChat/Rocket.Chat · error · Error

error-business-hour-finish-time-equals-start-time

error-business-hour-finish-time-equals-start-time

Error message

error-business-hour-finish-time-equals-start-time

What it means

Companion check in AbstractBusinessHour.convertWorkHours: an open work day whose start and finish map to the same UTC instant (e.g. 00:00 to 00:00) throws Error('error-business-hour-finish-time-equals-start-time'). A zero-length open period is treated as a configuration mistake; marking the day closed (open=false) is the supported way to express no service.

Source

Thrown at apps/meteor/server/lib/omnichannel/business-hour/AbstractBusinessHour.ts:103

				$set: businessHourData,
			} as UpdateFilter<ILivechatBusinessHour>); // TODO: Remove this cast when TypeScript is updated
			return businessHourData._id;
		}
		const { insertedId } = await this.BusinessHourRepository.insertOne(businessHourData);
		return insertedId;
	}

	private convertWorkHours(businessHourData: ILivechatBusinessHour): ILivechatBusinessHour {
		businessHourData.workHours.forEach((hour: any) => {
			const startUtc = moment.tz(`${hour.day}:${hour.start}`, 'dddd:HH:mm', businessHourData.timezone.name).utc();
			const finishUtc = moment.tz(`${hour.day}:${hour.finish}`, 'dddd:HH:mm', businessHourData.timezone.name).utc();

			if (hour.open && finishUtc.isBefore(startUtc)) {
				throw new Error('error-business-hour-finish-time-before-start-time');
			}

			if (hour.open && startUtc.isSame(finishUtc)) {
				throw new Error('error-business-hour-finish-time-equals-start-time');
			}

			hour.start = {
				time: hour.start,
				utc: {
					dayOfWeek: startUtc.clone().format('dddd'),
					time: startUtc.clone().format('HH:mm'),
				},
				cron: {
					dayOfWeek: this.formatDayOfTheWeekFromServerTimezoneAndUtcHour(startUtc, 'dddd'),
					time: this.formatDayOfTheWeekFromServerTimezoneAndUtcHour(startUtc, 'HH:mm'),
				},
			};
			hour.finish = {
				time: hour.finish,
				utc: {
					dayOfWeek: finishUtc.clone().format('dddd'),
					time: finishUtc.clone().format('HH:mm'),

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Set a real non-zero interval, or mark the day closed (open=false)
  2. Sanitize work-hours payloads in integrations: convert empty/zero intervals to open=false before submission
  3. Verify the business hour's timezone when distinct local times collapse to the same UTC time

Example fix

// before - open day with zero length
workHours: [{ day: 'Monday', open: true, start: '00:00', finish: '00:00' }]

// after - explicitly closed day
workHours: [{ day: 'Monday', open: false, start: '00:00', finish: '00:00' }]
Defensive patterns

Strategy: validation

Validate before calling

import moment from 'moment-timezone';

// In addition to the finish-after-start check, reject identical instants:
const isZeroLength = (h: { day: string; open: boolean; start: string; finish: string }, tz: string): boolean => {
  if (!h.open) return false;
  const s = moment.tz(`${h.day}:${h.start}`, 'dddd:HH:mm', tz).utc();
  const f = moment.tz(`${h.day}:${h.finish}`, 'dddd:HH:mm', tz).utc();
  return s.isSame(f);
};

if (businessHour.workHours.some((h) => isZeroLength(h, businessHour.timezone.name))) {
  // mark those days closed (open=false) instead of submitting zero-length open intervals
}

Type guard

const hasValidIntervals = (workHours: { day: string; open: boolean; start: string; finish: string }[], tz: string): boolean =>
  workHours.every((h) => {
    if (!h.open) return true;
    const s = moment.tz(`${h.day}:${h.start}`, 'dddd:HH:mm', tz).utc();
    const f = moment.tz(`${h.day}:${h.finish}`, 'dddd:HH:mm', tz).utc();
    return f.isAfter(s); // excludes both inverted and identical instants
  });

Prevention

When it happens

Trigger: Saving a business hour with open=true and identical start/finish values - commonly both '00:00' left at UI defaults - including cases where timezone conversion or rounding maps two distinct local times onto the same UTC instant.

Common situations: Admin UI defaults (00:00/00:00) left untouched on an 'open' day; copy-pasting a template day onto others; DST or timezone offsets collapsing a short interval (e.g. 30 minutes) to a single instant.

Related errors


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