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
- 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.
- Verify rid with rooms.info?roomId=<rid> before resetting.
- 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.
- 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
- Run key resets as a room member (e.g. room owner), not a mere permission holder.
- Verify rid via rooms.info before posting.
- After hitting this, restart the Meteor server before retrying — the room's lock is leaked.
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
- error-e2e-key-reset-in-progress
- error-action-not-allowed
- error-action-not-allowed
- error-creating-custom-user-status
- error-invalid-param
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)