RocketChat/Rocket.Chat · error · MeteorError
error-encrypted-private-rooms-enforced
error-encrypted-private-rooms-enforced
Error message
Workspace policy requires all private rooms to be encrypted.
What it means
Thrown by Rocket.Chat's 'beforeCreateRoom' E2E hook when a workspace has both E2E_Enable and E2E_Force_Encryption_For_Private_Rooms enabled and a caller creates a non-federated private channel (type 'p') with an explicit encrypted: false in extraData. Under this policy every new private room must be end-to-end encrypted, so an explicit opt-out is rejected rather than silently overridden. Omitting the encrypted flag never throws — the hook coerces it to true (beforeCreateRoom.ts sets extraData.encrypted = true).
Source
Thrown at apps/meteor/server/lib/e2e/beforeCreateRoom.ts:19
import { MeteorError } from '@rocket.chat/core-services';
import { settings } from '../../settings';
import { prepareCreateRoomCallback } from '../callbacks/beforeCreateRoomCallback';
prepareCreateRoomCallback.add(({ type, extraData }) => {
if (!settings.get<boolean>('E2E_Enable')) {
return;
}
if (
(type === 'd' && settings.get<boolean>('E2E_Enabled_Default_DirectRooms')) ||
(type === 'p' && settings.get<boolean>('E2E_Enabled_Default_PrivateRooms'))
) {
extraData.encrypted = extraData.encrypted ?? true;
}
if (type === 'p' && extraData.federated !== true && settings.get<boolean>('E2E_Force_Encryption_For_Private_Rooms')) {
if (extraData.encrypted === false) {
throw new MeteorError('error-encrypted-private-rooms-enforced', 'Workspace policy requires all private rooms to be encrypted.');
}
extraData.encrypted = true;
}
});
View on GitHub (pinned to 2a7de45707)
Solutions
- Remove encrypted:false from the createRoom / rooms.create payload and let the server default the room to encrypted — the policy hook sets encrypted=true itself.
- If the room genuinely must be unencrypted, create it as a public channel (type 'c') instead of 'p' — the policy only covers private rooms.
- Have a workspace admin disable E2E_Force_Encryption_For_Private_Rooms (Administration → Settings → E2E) if the policy was not intended.
- If this is a genuinely federated private room, pass federated: true — federated rooms are the only exemption to the force-encryption check.
Example fix
// before — throws error-encrypted-private-rooms-enforced on workspaces
// with E2E_Force_Encryption_For_Private_Rooms enabled
POST /api/v1/rooms.create
{ "name": "secret-project", "type": "p", "encrypted": false }
// after — omit the flag; the beforeCreateRoom hook sets encrypted=true per policy
POST /api/v1/rooms.create
{ "name": "secret-project", "type": "p" } Defensive patterns
Strategy: validation
Validate before calling
// server-side, before createRoom(...)
import { settings } from '../../settings';
const encryptionEnforced = (): boolean =>
settings.get<boolean>('E2E_Enable') === true &&
settings.get<boolean>('E2E_Force_Encryption_For_Private_Rooms') === true;
if (type === 'p' && extraData.federated !== true && encryptionEnforced()) {
delete extraData.encrypted; // never send an explicit opt-out; the hook sets encrypted=true
} Type guard
const isExplicitEncryptionOptOut = (
extraData: { encrypted?: boolean },
): extraData is { encrypted: false } => extraData.encrypted === false; Try / catch
try {
await Meteor.callAsync('createPrivateGroup', { name, members, encrypted: false });
} catch (error) {
if (error instanceof Meteor.Error && error.error === 'error-encrypted-private-rooms-enforced') {
// policy workspace: retry without the explicit opt-out — the server defaults to encrypted
return Meteor.callAsync('createPrivateGroup', { name, members });
}
throw error;
} Prevention
- Treat optional booleans as 'unset by default' — omit encrypted unless the caller made an explicit user-facing choice.
- Feature-detect the workspace policy (E2E_Enable + E2E_Force_Encryption_For_Private_Rooms) before offering an 'unencrypted private channel' option in UI or integrations.
- Do not share hardcoded request payloads across environments; a payload recorded on a non-E2E workspace violates policy on an E2E-enforcing one.
- Remember federated private rooms are the only exemption to the force-encryption check — and federation is a real setup, not a bypass flag.
When it happens
Trigger: Calling createRoom('p', name, user, members, readOnly, { encrypted: false }) server-side, or any surface built on it (Meteor method createPrivateGroup, REST POST /api/v1/rooms.create with type 'p' and encrypted:false), while E2E_Enable=true, E2E_Force_Encryption_For_Private_Rooms=true, and extraData.federated !== true. Only an explicit false triggers the throw; encrypted: undefined or true is accepted and coerced to true.
Common situations: An admin enables 'Force encryption for private rooms' after integration code (apps-engine apps, bots, provisioning scripts, test fixtures) was already hardcoding encrypted:false so the flag is always present in payloads. Also hit when copying request payloads between workspaces/environments where the E2E policy differs, or in fixtures that pin every optional field explicitly.
Related errors
- error-encrypted-private-rooms-enforced-discussion
- error-e2e-enabled
- The required "roomId" or "roomName" param provided does not
- error-invalid-user
- error-invalid-room
AI-assisted analysis of RocketChat/Rocket.Chat@2a7de45707 (2026-08-21).
Data as JSON: /api/errors/23cc31f55b7166fb.
Report an issue: GitHub.