{"record":{"id":"2f1de126885c682f","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-department","errorCode":"error-invalid-department","errorMessage":"Invalid department","messagePattern":"Invalid department","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/ee/server/meteor-methods/saveCannedResponse.ts","lineNumber":67,"sourceCode":"\n\t// to avoid inconsistencies\n\tif (responseData.scope === 'user') {\n\t\tdelete responseData.departmentId;\n\t}\n\t// TODO: check if the department i'm trying to save is a department i can interact with\n\n\t// check if the response already exists and we're not updating one\n\tconst duplicateShortcut = await CannedResponse.findOneByShortcut(responseData.shortcut, {\n\t\tprojection: { _id: 1 },\n\t});\n\tif ((!_id && duplicateShortcut) || (_id && duplicateShortcut && duplicateShortcut._id !== _id)) {\n\t\tthrow new Meteor.Error('error-invalid-shortcut', 'Shortcut provided already exists', {\n\t\t\tmethod: 'saveCannedResponse',\n\t\t});\n\t}\n\n\tif (responseData.scope === 'department' && !responseData.departmentId) {\n\t\tthrow new Meteor.Error('error-invalid-department', 'Invalid department', {\n\t\t\tmethod: 'saveCannedResponse',\n\t\t});\n\t}\n\n\tif (\n\t\tresponseData.departmentId &&\n\t\t!(await LivechatDepartment.findOneById<Pick<ILivechatDepartment, '_id'>>(responseData.departmentId, { projection: { _id: 1 } }))\n\t) {\n\t\tthrow new Meteor.Error('error-invalid-department', 'Invalid department', {\n\t\t\tmethod: 'saveCannedResponse',\n\t\t});\n\t}\n\n\tlet result: Omit<IOmnichannelCannedResponse, '_updatedAt' | '_createdAt'> & { _createdAt?: Date };\n\n\tif (_id) {\n\t\tconst cannedResponse = await CannedResponse.findOneById(_id);\n\t\tif (!cannedResponse) {","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/ee/server/meteor-methods/saveCannedResponse.ts#L49-L85","documentation":"Thrown by the saveCannedResponse method when a canned response is saved with scope === 'department' but no departmentId value is supplied. Rocket.Chat requires a department-scoped canned response to be tied to a concrete LivechatDepartment record, so a missing departmentId is treated as invalid input rather than defaulted. This fires before any DB lookup of the department.","triggerScenarios":"Calling saveCannedResponse (or the cannedResponse.save meteor method / REST endpoint that delegates to it) with responseData.scope set to the literal string 'department' while omitting or leaving empty responseData.departmentId.","commonSituations":"UI form that lets the agent pick a scope dropdown but does not require the department selector when 'department' is chosen; a migration or seed script that hard-codes scope:'department' without setting departmentId; front-end bug where the department select is disabled/hidden but its value is not cleared.","solutions":["When scope === 'department', always send a non-empty departmentId alongside the request.","If the caller does not have a department, set scope to 'user' or 'global' instead of 'department'.","In the form layer, make the department field required (and re-enabled) whenever the scope selector equals 'department'.","Unit-test the save path with scope:'department' and an empty departmentId to fail fast at the API boundary."],"exampleFix":"// before\nawait saveCannedResponse(userId, { shortcut: 'hi', text: 'Hello', scope: 'department' });\n\n// after\nawait saveCannedResponse(userId, { shortcut: 'hi', text: 'Hello', scope: 'department', departmentId });","handlingStrategy":"validation","validationCode":"function validateCannedResponseInput(data: { scope: string; departmentId?: string }): string | null {\n  if (data.scope === 'department' && !data.departmentId) {\n    return 'A departmentId is required when scope is \"department\".';\n  }\n  return null;\n}\n// const err = validateCannedResponseInput(data); if (err) throw new Error(err);","typeGuard":"function isDepartmentScopedPayload(data: unknown): data is { scope: 'department'; departmentId: string } {\n  return typeof data === 'object' && data !== null\n    && (data as any).scope === 'department'\n    && typeof (data as any).departmentId === 'string'\n    && (data as any).departmentId.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Make the department selector a required field in the UI whenever scope is 'department'.","Run the same scope/departmentId check in a shared validator on both client and server.","Add a unit test that asserts the error for scope:'department' without departmentId."],"tags":["omnichannel","canned-response","validation","department"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}