RocketChat/Rocket.Chat · error · Error

error-business-hour-name-already-in-use

Error message

error-business-hour-name-already-in-use

What it means

Thrown by CustomBusinessHour.saveBusinessHour in Custom.ts:44 when a different business hour already uses the requested name. It looks up by {name} and only allows reuse when the found _id equals the one being saved (i.e. renaming to your own name is fine). NOTE: plain `new Error('error-business-hour-name-already-in-use')` — no Meteor.Error, so the code lives only in the message string.

Source

Thrown at apps/meteor/ee/server/lib/omnichannel/business-hour/Custom.ts:44

		const businessHour = await this.BusinessHourRepository.findOneById(id);
		if (!businessHour) {
			return null;
		}

		businessHour.departments = await LivechatDepartment.findByBusinessHourId(businessHour._id, {
			projection: { name: 1 },
		}).toArray();
		return businessHour;
	}

	async saveBusinessHour(businessHour: ILivechatBusinessHour & IBusinessHoursExtraProperties): Promise<ILivechatBusinessHour> {
		const existingBusinessHour = (await this.BusinessHourRepository.findOne(
			{ name: businessHour.name },
			{ projection: { _id: 1 } },
		)) as ILivechatBusinessHour;
		if (existingBusinessHour && existingBusinessHour._id !== businessHour._id) {
			throw new Error('error-business-hour-name-already-in-use');
		}
		const { timezoneName, departmentsToApplyBusinessHour, ...businessHourData } = businessHour;
		businessHourData.timezone = {
			name: timezoneName,
			utc: this.getUTCFromTimezone(timezoneName),
		};
		const businessHourToReturn = { ...businessHourData, departmentsToApplyBusinessHour };
		delete businessHourData.departments;

		const businessHourId = await this.baseSaveBusinessHour(businessHourData);

		// Internal callers (e.g. the DST verifier) re-save business hours without the
		// departments field; only reconcile department links when it is provided,
		// otherwise an internal re-save would unlink every department.
		if (departmentsToApplyBusinessHour !== undefined) {
			const departments = departmentsToApplyBusinessHour.split(',').filter(Boolean);
			const currentDepartments = (
				await LivechatDepartment.findByBusinessHourId(businessHourId, {

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Choose a unique business-hour name.
  2. Pre-list business hours and validate the name client-side before save.
  3. Match by message string 'error-business-hour-name-already-in-use' in the catch (no code available).

Example fix

// before
await businessHour.saveBusinessHour(payload);

// after
const clash = await LivechatBusinessHours.findOne({ name: payload.name }, { projection: { _id: 1 } });
if (clash && clash._id !== payload._id) {
  throw new Error('name taken');
}
await businessHour.saveBusinessHour(payload);
Defensive patterns

Strategy: validation

Validate before calling

const clash = await LivechatBusinessHours.findOne({ name: payload.name }, { projection: { _id: 1 } });
if (clash && clash._id !== payload._id) throw new Error('name taken');

Type guard

const isNameUnique = async (name: string, selfId?: string) => {
  const c = await LivechatBusinessHours.findOne({ name }, { projection: { _id: 1 } });
  return !c || c._id === selfId;
};

Try / catch

try { await bh.saveBusinessHour(payload); }
catch (e) {
  if (e instanceof Error && e.message === 'error-business-hour-name-already-in-use') { /* rename */ return; }
  throw e;
}

Prevention

When it happens

Trigger: Creating a business hour with a name already taken, or renaming an existing hour to another hour's name. The check ignores _id equality for new records (businessHour._id undefined/falsy).

Common situations: Default 'Business Hours' name reused; admin copy operation that does not rename; CI fixture collision.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/9ec2190c05ba7268. Report an issue: GitHub.