RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-room

error-invalid-room

Error message

Invalid room

What it means

Thrown by updateIncomingIntegration while resolving each channel target: after stripping the leading '#'/'@', '#x' must match a Room by _id or name and '@x' a User by _id or username. A miss means the referenced room or user does not exist on this server. Note the update payload replaces the whole channel list, so one dead target blocks the entire update.

Source

Thrown at apps/meteor/server/meteor-methods/integrations/incoming/updateIncomingIntegration.ts:130

		const channelType = channel[0];
		channel = channel.slice(1);
		let record;

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

		if (!record) {
			throw new Meteor.Error('error-invalid-room', 'Invalid room', {
				method: 'updateIncomingIntegration',
			});
		}

		if (
			!(await hasAllPermissionAsync(userId, ['manage-incoming-integrations', 'manage-own-incoming-integrations'])) &&
			!(await Subscriptions.findOneByRoomIdAndUserId(record._id, userId, { projection: { _id: 1 } }))
		) {
			throw new Meteor.Error('error-invalid-channel', 'Invalid Channel', {
				method: 'updateIncomingIntegration',
			});
		}
	}

	const username = 'username' in integration ? integration.username : currentIntegration.username;
	const user = await Users.findOneByUsername(username, { projection: { _id: 1, username: 1 } });

	if (!user) {

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Use exact current room names (lowercase slug, no '#') and existing @usernames in every segment
  2. Remove or recreate dead targets before submitting the update
  3. Pre-verify each target: GET /api/v1/channels.info?roomName=... or Rooms.findOne({ name }) server-side

Example fix

// before
await Meteor.callAsync('updateIncomingIntegration', id, { ...payload, channel: '#renamed-room,@gone.user' });
// after: only existing targets
await Meteor.callAsync('updateIncomingIntegration', id, { ...payload, channel: '#new-room-name' });
Defensive patterns

Strategy: validation

Validate before calling

// server-side caller: resolve every target before the update
import { Rooms, Users } from '@rocket.chat/models';
for (const entry of channelList.split(',')) {
  const type = entry.trim()[0]; const name = entry.trim().slice(1);
  const exists = type === '#' ? !!(await Rooms.findOne({ $or: [{ _id: name }, { name }] })) : !!(await Users.findOne({ $or: [{ _id: name }, { username: name }] }));
  if (!exists) throw new Error(`target no longer exists: ${entry.trim()}`);
}
await Meteor.callAsync('updateIncomingIntegration', integrationId, { ...integration, channel: channelList });

Try / catch

try {
  await Meteor.callAsync('updateIncomingIntegration', integrationId, integration);
} catch (err) {
  if (err instanceof Meteor.Error && err.error === 'error-invalid-room') { /* drop dead targets from the list, notify user */ }
}

Prevention

When it happens

Trigger: Updating with '#renamed-room' after the room was renamed; '#archived-channel' that was deleted; '@departed.user' whose account was removed; a typo such as '#dev-ops' vs '#devops'.

Common situations: Long-lived integrations whose channel lists go stale as rooms are reorganized; bulk edit tooling applying one channel set to many workspaces where not every room exists; copy-paste between environments.

Related errors


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