RocketChat/Rocket.Chat · error · Error
Business hour ${department.businessHourId} not found
Error message
Business hour ${department.businessHourId} not found What it means
In onDepartmentDisabled, when the disabled department was the last one using the business hour, the hour is disabled and then re-fetched with findOneById(department.businessHourId) before reopening logic. If that re-fetch returns null, it throws 'Business hour ${id} not found'.
Source
Thrown at apps/meteor/ee/server/lib/omnichannel/business-hour/Multiple.ts:206
const defaultBH = await this.BusinessHourRepository.findOneDefaultBusinessHour();
if (!defaultBH) {
throw new Error('Default business hour not found');
}
await this.UsersRepository.closeAgentsBusinessHoursByBusinessHourIds([businessHour._id, defaultBH._id]);
// If i'm the only one, disable the business hour
const imTheOnlyOne = !(await LivechatDepartment.countByBusinessHourIdExcludingDepartmentId(businessHour._id, department._id));
if (imTheOnlyOne) {
bhLogger.warn({
msg: 'onDepartmentDisabled: department is the only one on business hour, disabling it',
departmentId: department._id,
businessHourId: businessHour._id,
});
await this.BusinessHourRepository.disableBusinessHour(businessHour._id);
businessHour = await this.BusinessHourRepository.findOneById(department.businessHourId);
if (!businessHour) {
throw new Error(`Business hour ${department.businessHourId} not found`);
}
}
// start default business hour and this BH if needed
if (!settings.get('Livechat_enable_business_hours')) {
return;
}
const businessHourToOpen = await filterBusinessHoursThatMustBeOpened([businessHour, defaultBH]);
for await (const bh of businessHourToOpen) {
await openBusinessHour(bh, false);
}
await makeAgentsUnavailableBasedOnBusinessHour();
await businessHourManager.restartCronJobsIfNecessary();
}
async onDepartmentArchived(department: Pick<ILivechatDepartment, '_id' | 'businessHourId'>): Promise<void> {View on GitHub (pinned to b2c16d5842)
Solutions
- Ensure departments are unlinked from a business hour before deleting that hour
- Clear stale businessHourId references on departments (set to null) so onDepartmentDisabled does not chase missing records
- Retry the department-disable after the concurrent business-hour operation settles
Defensive patterns
Strategy: try-catch
Validate before calling
const bh = await BusinessHourRepository.findOneById(department.businessHourId);
if (!bh) {
// clear the stale reference: await LivechatDepartment.removeBusinessHourFromDepartmentsByIdsAndBusinessHourId([department._id], department.businessHourId);
}
await onDepartmentDisabled(department); Try / catch
try {
await onDepartmentDisabled(department);
} catch (err) {
if (err instanceof Error && /^Business hour .+ not found$/.test(err.message)) {
// stale businessHourId on the department: clear the reference and retry
} else {
throw err;
}
} Prevention
- Unlink departments from a business hour before deleting the hour
- Avoid concurrent admin operations on the same department/business hour pair
- Periodically audit departments for dangling businessHourId references
When it happens
Trigger: Disabling the last department of a business hour while the business-hour record disappears between the disable and the re-fetch (concurrent removal or data inconsistency).
Common situations: Concurrent admin actions deleting the same business hour; inconsistent data after partial migration; the department still referencing a deleted business hour id.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Default business hour not found
- No valid agent found
- error-business-hour-name-already-in-use
- This_conversation_is_already_closed
- error-invalid-department
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/23f45e854804098a.
Report an issue: GitHub.