RocketChat/Rocket.Chat · error · Error

error-action-not-allowed

error-action-not-allowed

Error message

error-action-not-allowed

What it means

Thrown by the guest message-edit flow (guests.ts) when `Message_AllowEditing` is false OR the original message's owner (`originalMessage.u._id`) is not the guest making the request. Guests may only edit their own messages and only while global message editing is enabled; either condition failing aborts before `updateMessageFunc` runs.

Source

Thrown at apps/meteor/server/lib/omnichannel/messages.ts:112

		void callbacks.run('livechat.offlineMessage', data);
	});
}

export async function updateMessage({ guest, message }: { guest: ILivechatVisitor; message: AtLeast<IMessage, '_id' | 'msg' | 'rid'> }) {
	// TODO: Remove check
	check(message, Match.ObjectIncluding({ _id: String }));

	const originalMessage = await Messages.findOneById<Pick<IMessage, 'u' | '_id'>>(message._id, { projection: { u: 1 } });
	if (!originalMessage?._id) {
		return;
	}

	// TODO: shouldn't this happen inside updateMessageFunc?
	const editAllowed = settings.get('Message_AllowEditing');
	const editOwn = originalMessage.u && originalMessage.u._id === guest._id;

	if (!editAllowed || !editOwn) {
		throw new Error('error-action-not-allowed');
	}

	// TODO: Apps sends an `any` object and apparently we just check for _id being present
	// while updateMessage expects AtLeast<id, msg, rid>
	await updateMessageFunc(message, guest as unknown as IUser);

	return true;
}

export async function deleteMessage({ guest, message }: { guest: ILivechatVisitor; message: IMessage }) {
	const deleteAllowed = settings.get<boolean>('Message_AllowDeleting');
	const editOwn = message.u && message.u._id === guest._id;

	if (!deleteAllowed || !editOwn) {
		throw new Error('error-action-not-allowed');
	}

	// TODO: we shouldn't do this :(

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Enable the `Message_AllowEditing` setting if guests should edit
  2. Hide/disable the edit action in the widget when editing is not allowed or the message is not the guest's own
  3. Ensure the edit call authenticates as the same guest who authored the message

Example fix

// before
await updateMessage({ guest, message: { _id: msgId, msg: 'new text' } });

// after
const original = await Messages.findOneById(msgId, { projection: { u: 1 } });
if (!settings.get('Message_AllowEditing') || original?.u?._id !== guest._id) {
  throw new Error('Editing this message is not allowed');
}
await updateMessage({ guest, message: { _id: msgId, msg: 'new text' } });
Defensive patterns

Strategy: validation

Validate before calling

const original = await Messages.findOneById<Pick<IMessage, 'u'>>(message._id, { projection: { u: 1 } });
if (!settings.get('Message_AllowEditing') || original?.u?._id !== guest._id) {
  throw new Error('Editing is not allowed for this message');
}
await updateMessage({ guest, message });

Type guard

const guestOwnsMessage = (guest: { _id: string }, msg: { u?: { _id?: string } }): boolean =>
  msg.u?._id === guest._id;

Try / catch

try {
  await updateMessage({ guest, message });
} catch (e) {
  if (e instanceof Error && e.message === 'error-action-not-allowed') {
    // hide edit affordance; editing disabled or message not the guest's own
  }
}

Prevention

When it happens

Trigger: A livechat visitor widget calling the edit-message API after an admin disabled 'Message_AllowEditing'; or an integration editing a message on behalf of a guest who did not send it (mismatched guest._id vs message.u._id).

Common situations: Workspace policy turns off message editing and the widget's edit button was not hidden; messages edited after agent takeover where ownership metadata changed; tests replaying guest traffic with synthetic ids.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/bceef549c9650d39. Report an issue: GitHub.