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
- Set a real non-zero interval, or mark the day closed (open=false)
- Sanitize work-hours payloads in integrations: convert empty/zero intervals to open=false before submission
- 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
- Default new work-hour rows to open=false so untouched 00:00-00:00 rows never submit as open
- When duplicating days, verify start and finish differ on every open row
- Express 'closed all day' as open=false, never as a zero-length open interval
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
- error-business-hour-finish-time-before-start-time
- Invalid type
- error-duplicated-sla
- error-business-hour-name-already-in-use
- error-invalid-room
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/767725af5b3add68.
Report an issue: GitHub.