RocketChat/Rocket.Chat · error · Meteor.Error
error-encrypted-private-rooms-enforced-discussion
error-encrypted-private-rooms-enforced-discussion
Error message
Workspace policy requires all private rooms to be encrypted. To create this discussion, make the parent channel public or enable encryption on it.
What it means
Thrown by the createDiscussion server flow when the resolved discussion type is private ('p', inherited from a private parent channel via roomCoordinator.getDiscussionType), the effective encrypted flag is false, and the workspace enforces E2E_Enable plus E2E_Force_Encryption_For_Private_Rooms. Discussions inherit encryption from the parent room (createDiscussion.ts:116-118 defaults encrypted to Boolean(parentRoom.encrypted)), so an unencrypted private parent would produce an unencrypted private discussion, which the policy forbids. Passing encrypted: true explicitly bypasses this check, but then the call must omit reply, because encrypted discussions reject initial replies (createDiscussion.ts:120 throws error-invalid-arguments).
Source
Thrown at apps/meteor/server/meteor-methods/messages/createDiscussion.ts:160
const invitedUsers = message ? [message.u.username, ...users] : users;
const type = await roomCoordinator.getRoomDirectives(parentRoom.t).getDiscussionType(parentRoom);
const description = parentRoom.encrypted ? '' : message?.msg;
const discussionTopic = topic || parentRoom.name;
if (!type) {
throw new Meteor.Error('error-invalid-type', 'Cannot define discussion room type', {
method: 'DiscussionCreation',
});
}
if (
type === 'p' &&
!encrypted &&
settings.get<boolean>('E2E_Enable') &&
settings.get<boolean>('E2E_Force_Encryption_For_Private_Rooms')
) {
throw new Meteor.Error(
'error-encrypted-private-rooms-enforced-discussion',
'Workspace policy requires all private rooms to be encrypted. To create this discussion, make the parent channel public or enable encryption on it.',
{ method: 'DiscussionCreation' },
);
}
const discussion = await createRoom(
type,
name,
user,
[...new Set(invitedUsers)].filter(Boolean),
false,
false,
{
fname: discussionName,
description, // TODO discussions remove
topic: discussionTopic,
prid,View on GitHub (pinned to 2a7de45707)
Solutions
- Enable E2E encryption on the parent private channel first (room header → Encryption), then retry — the discussion inherits encrypted=true and passes.
- Pass encrypted: true in the createDiscussion call so the discussion itself is E2E even though the parent is not; you must also omit the reply field, since encrypted discussions cannot take an initial reply.
- Make the parent channel public (type 'c') and retry — public rooms sit outside the force-encryption policy.
- Or have a workspace admin disable E2E_Force_Encryption_For_Private_Rooms if unencrypted private discussions must remain possible.
Example fix
// before — parent room is private + unencrypted, policy enabled → throws
// error-encrypted-private-rooms-enforced-discussion
await Meteor.callAsync('createDiscussion', {
prid: 'parentRoomId', t_name: 'Sprint sync', users: ['alice'], reply: 'kickoff',
});
// after — opt the discussion into E2E explicitly and drop the initial reply
await Meteor.callAsync('createDiscussion', {
prid: 'parentRoomId', t_name: 'Sprint sync', users: ['alice'], encrypted: true,
}); Defensive patterns
Strategy: validation
Validate before calling
import { Rooms } from '@rocket.chat/models';
import { settings } from '../../settings';
const privateDiscussionBlockedByPolicy = async (
prid: string,
encrypted?: boolean,
): Promise<boolean> => {
const parent = await Rooms.findOneById(prid, { projection: { t: 1, encrypted: 1 } });
if (!parent || parent.t !== 'p') return false; // only private parents get a 'p' discussion
const effectiveEncrypted = typeof encrypted === 'boolean' ? encrypted : Boolean(parent.encrypted);
return (
!effectiveEncrypted &&
settings.get<boolean>('E2E_Enable') === true &&
settings.get<boolean>('E2E_Force_Encryption_For_Private_Rooms') === true
);
};
if (await privateDiscussionBlockedByPolicy(prid, encrypted)) {
// encrypt the parent, pass encrypted: true (with no reply), or block the action in the UI
} Try / catch
try {
await Meteor.callAsync('createDiscussion', { prid, t_name, users });
} catch (error) {
if (error instanceof Meteor.Error && error.error === 'error-encrypted-private-rooms-enforced-discussion') {
// offer the user the two policy-compliant paths: encrypt the parent channel,
// or retry with encrypted: true (and no initial reply)
return Meteor.callAsync('createDiscussion', { prid, t_name, users, encrypted: true });
}
throw error;
} Prevention
- Never force encrypted:false for discussions — omit the field and inherit encryption from the parent room.
- When the policy is on, disable the discussion-creation affordance on private unencrypted parents, or preselect encrypted:true.
- Encrypted discussions cannot receive an initial reply — whenever you pass encrypted:true, drop the reply argument (a separate error-invalid-arguments is thrown otherwise).
- Verify pmid belongs to prid before calling; adjacent argument mismatches throw their own errors earlier in the same method.
When it happens
Trigger: Meteor method createDiscussion (deprecated in 9.0.0 in favor of POST /v1/rooms.createDiscussion) with prid/pmid pointing at a private, unencrypted channel while E2E_Enable and E2E_Force_Encryption_For_Private_Rooms are both true — either with no encrypted argument (inherits false from the parent) or with an explicit encrypted:false. The check fires after type resolution and after the reply/encrypted mutual-exclusion check.
Common situations: An admin flips on forced encryption on an existing workspace where users already have private unencrypted channels — every 'Create discussion' action on those channels now fails until parents are encrypted. Also hit by REST integrations that never send the encrypted field for private parents, or when replaying a createDiscussion payload recorded on a non-E2E workspace against an E2E-enforcing one.
Related errors
- error-encrypted-private-rooms-enforced
- error-e2e-enabled
- The required "roomId" or "roomName" param provided does not
- error-invalid-user
- error-ai-not-enabled
AI-assisted analysis of RocketChat/Rocket.Chat@2a7de45707 (2026-08-21).
Data as JSON: /api/errors/5808b86032b2060d.
Report an issue: GitHub.