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

  1. If the record was deleted, switch to the create flow (omit _id) instead of update.
  2. Re-fetch the canned response by _id before showing the edit form; disable submit if it is gone.
  3. On catching this error, reload the canned-response list in the UI and have the user re-open the item.
  4. 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

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


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