RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-channel

error-invalid-channel

Error message

Invalid Channel

What it means

Thrown by _verifyUserHasPermissionForChannels when a channel entry is one of the workspace-wide scope tokens 'all_private_groups' or 'all_direct_messages' but the creating user lacks the manage-outgoing-integrations permission. Listening across every private group or DM is privileged, so only users holding that permission may register such an integration; 'all_public_channels' is explicitly exempt and needs no permission.

Source

Thrown at apps/meteor/server/lib/integrations/lib/validateOutgoingIntegration.ts:60

		});
	}

	integration.urls = integration.urls.filter((url) => url && url.trim() !== '');

	if (integration.urls.length === 0) {
		throw new Meteor.Error('error-invalid-urls', 'Invalid URLs', {
			function: 'validateOutgoing._verifyRequiredFields',
		});
	}
}

async function _verifyUserHasPermissionForChannels(userId: IUser['_id'], channels: string[]): Promise<void> {
	for (let channel of channels) {
		if (scopedChannels.includes(channel)) {
			if (channel === 'all_public_channels') {
				// No special permissions needed to add integration to public channels
			} else if (!(await hasPermissionAsync(userId, 'manage-outgoing-integrations'))) {
				throw new Meteor.Error('error-invalid-channel', 'Invalid Channel', {
					function: 'validateOutgoing._verifyUserHasPermissionForChannels',
				});
			}
		} else {
			let record;
			const channelType = channel[0];
			channel = channel.substr(1);

			switch (channelType) {
				case '#':
					record = await Rooms.findOne({
						$or: [{ _id: channel }, { name: channel }],
					});
					break;
				case '@':
					record = await Users.findOne({
						$or: [{ _id: channel }, { username: channel }],
					});

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Grant the creator's role manage-outgoing-integrations (Admin -> Permissions), then retry
  2. Or scope the integration to explicit channels ('#private-room') the user belongs to instead of the all_* token
  3. Use 'all_public_channels' when public-channel coverage is enough — it requires no permission

Example fix

// before — non-admin, workspace-wide private scope
{ channel: 'all_private_groups' }

// after — grant the permission, or list rooms explicitly
// Admin -> Permissions -> role 'user' -> manage-outgoing-integrations = On
{ channel: '#private-room' }
Defensive patterns

Strategy: validation

Validate before calling

const privilegedScopes = ['all_private_groups', 'all_direct_messages'];
if (channels.some((c) => privilegedScopes.includes(c)) && !(await hasPermissionAsync(userId, 'manage-outgoing-integrations'))) {
  throw new Error('all_private_groups / all_direct_messages require manage-outgoing-integrations');
}

Prevention

When it happens

Trigger: A non-admin calls integrations.create/update with channel containing 'all_private_groups' or 'all_direct_messages' (case-sensitive as written in the channel CSV) while their role lacks manage-outgoing-integrations.

Common situations: Power users trying to mirror all DMs or private channels into an external tool; permission sets cloned from a bot role that never included integration management; forgetting that only the public-channel scope is unprivileged.

Related errors


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