RocketChat/Rocket.Chat · error · Error

error-not-allowed

Error message

error-not-allowed

What it means

Thrown by POST e2e.resetRoomKey when the caller holds the toggle-room-e2e-encryption permission but still cannot access the room: after the permission check passes and the lock is taken, the endpoint runs canAccessRoomIdAsync(rid, this.userId) and throws new Error('error-not-allowed') when the user is not in the room or rid is invalid. Typical for admins with a global permission who are not members of the target room. Because it is thrown after LockMap.set(rid, true) and outside the try/finally, this path also leaves the room's reset lock stuck until server restart (every later call fails with error-e2e-key-reset-in-progress).

Solutions

  1. Run the reset as a user who is a member of the room (e.g. the room owner), or add the service account to the room first.
  2. Verify rid with rooms.info?roomId=<rid> before resetting.
  3. If you already hit this, note the room lock is now wedged in that server process — restart the Meteor app to clear LockMap, then retry as a member.
  4. Track upstream: a server-side fix should move the access check before LockMap.set or wrap it in the finally cleanup.

Example fix

// before (admin token, not a room member)
await api.post('e2e.resetRoomKey', { rid, e2eKey, e2eKeyId });

// after — ensure the caller can access the room before resetting
const info = await api.get('rooms.info', { roomId: rid });
if (!info.room) throw new Error('room not accessible');
await api.post('e2e.resetRoomKey', { rid, e2eKey, e2eKeyId });
Defensive patterns

Strategy: validation

Validate before calling

async function resetE2EKeyAsMember(api, rid: string, e2eKey: string, e2eKeyId: string) {
  try { await api.get('rooms.info', { roomId: rid }); }
  catch { throw new Error(`caller cannot access room ${rid} — join it first`); }
  return api.post('e2e.resetRoomKey', { rid, e2eKey, e2eKeyId });
}

Try / catch

try { await api.post('e2e.resetRoomKey', body); }
catch (e) {
  if (e.message === 'error-not-allowed') { /* permission ok but no room membership — join room, then note server lock may be wedged */ } else throw e;
}

Prevention

When it happens

Trigger: An admin (global toggle-room-e2e-encryption) calling e2e.resetRoomKey for a room they never joined, or with a nonexistent rid; the room being deleted between requests.

Common situations: Ops scripts rotating keys for all encrypted rooms using one admin service account that has permission but no membership; typos in rid; targets that are DMs the admin is not part of.

Related errors


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

Appendix: source

Thrown at apps/meteor/server/api/v1/e2e.ts:451

				200: ajv.compile<void>({
					type: 'object',
				}),
			},
		},

		async function action() {
			const { rid, e2eKey, e2eKeyId } = this.bodyParams;
			if (!(await hasPermissionAsync(this.user, 'toggle-room-e2e-encryption', rid))) {
				return API.v1.forbidden('error-not-allowed');
			}
			if (LockMap.has(rid)) {
				throw new Error('error-e2e-key-reset-in-progress');
			}

			LockMap.set(rid, true);

			if (!(await canAccessRoomIdAsync(rid, this.userId))) {
				throw new Error('error-not-allowed');
			}

			try {
				await resetRoomKey(rid, this.userId, e2eKey, e2eKeyId);
				return API.v1.success();
			} catch (e) {
				console.error(e);
				return API.v1.failure('error-e2e-key-reset-failed');
			} finally {
				LockMap.delete(rid);
			}
		},
	)
	.post(
		'e2e.setUserPublicAndPrivateKeys',
		{
			authRequired: true,
			body: ise2eSetUserPublicAndPrivateKeysParamsPOST,

View on GitHub (pinned to b2c16d5842)