RocketChat/Rocket.Chat · error · Meteor.Error
error-canned-response-not-found
error-canned-response-not-found
Error message
Canned Response not found
What it means
Thrown by saveCannedResponse when an _id is passed (indicating an update) but CannedResponse.findOneById(_id) returns null. The method cannot update a canned response that does not exist, so it aborts. This guards against updating a record that was deleted between the client loading it and submitting the edit.
Source
Thrown at apps/meteor/ee/server/meteor-methods/saveCannedResponse.ts:86
method: 'saveCannedResponse',
});
}
if (
responseData.departmentId &&
!(await LivechatDepartment.findOneById<Pick<ILivechatDepartment, '_id'>>(responseData.departmentId, { projection: { _id: 1 } }))
) {
throw new Meteor.Error('error-invalid-department', 'Invalid department', {
method: 'saveCannedResponse',
});
}
let result: Omit<IOmnichannelCannedResponse, '_updatedAt' | '_createdAt'> & { _createdAt?: Date };
if (_id) {
const cannedResponse = await CannedResponse.findOneById(_id);
if (!cannedResponse) {
throw new Meteor.Error('error-canned-response-not-found', 'Canned Response not found', {
method: 'saveCannedResponse',
});
}
result = await CannedResponse.updateCannedResponse(_id, {
...responseData,
...(cannedResponse.scope === 'user' && { userId: cannedResponse.userId }),
createdBy: cannedResponse.createdBy,
});
} else {
const user = await Users.findOneById(userId);
const data = {
...responseData,
...(responseData.scope === 'user' && { userId: user?._id }),
createdBy: { _id: user?._id || '', username: user?.username || '' },
_createdAt: new Date(),
};View on GitHub (pinned to f9d3ec372b)
Solutions
- If the record was deleted, switch to the create flow (omit _id) instead of update.
- Re-fetch the canned response by _id before showing the edit form; disable submit if it is gone.
- On catching this error, reload the canned-response list in the UI and have the user re-open the item.
- Verify the _id string is correct and from the same workspace.
Example fix
// before
await saveCannedResponse(userId, data, staleId);
// after
const existing = await CannedResponse.findOneById(staleId);
if (!existing) {
await saveCannedResponse(userId, data); // create instead
} else {
await saveCannedResponse(userId, data, staleId);
} Defensive patterns
Strategy: validation
Validate before calling
async function cannedResponseExists(_id: string): Promise<boolean> {
const existing = await CannedResponse.findOneById(_id, { projection: { _id: 1 } });
return Boolean(existing);
} Try / catch
try {
await saveCannedResponse(userId, data, _id);
} catch (e) {
if (e.error === 'error-canned-response-not-found') {
// fall back to create flow
await saveCannedResponse(userId, data);
} else throw e;
} Prevention
- Re-fetch the canned response by _id before opening the edit form.
- Disable submit if the underlying record disappears (use a live subscription).
- Handle concurrent deletes gracefully by falling back to create.
When it happens
Trigger: Calling saveCannedResponse with a third _id argument that does not match any document in the canned_responses collection; editing a canned response in one tab while another tab/admin deletes it.
Common situations: Stale _id held in client state after deletion; concurrent admin sessions; restoring from an old backup of the front-end cache; test fixtures that reference a non-inserted id.
Related errors
- error-contact-not-found
- error-visitor-not-found
- invalid-user
- error-invalid-provider
- error-invalid-department
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/343afb98ee142483.
Report an issue: GitHub.