RocketChat/Rocket.Chat · error · Error

room-not-on-team

Error message

room-not-on-team

What it means

Thrown by TeamService.updateRoom (apps/meteor/server/services/team/service.ts:464) when the fetched room document has no teamId field — the channel does not belong to any team. updateRoom only manages team channel properties (teamDefault / auto-join), so a non-team room is a state error rather than a not-found one. The room and user checks have already passed when this throws.

Source

Thrown at apps/meteor/server/services/team/service.ts:464

		const room = await Rooms.findOneById(rid);
		if (!room) {
			throw new Error('invalid-room');
		}

		const user = await Users.findOneById(uid);
		if (!user) {
			throw new Error('invalid-user');
		}
		if (!canUpdateAnyRoom) {
			const canSeeRoom = await Authorization.canAccessRoom(room, user);
			if (!canSeeRoom) {
				throw new Error('invalid-room');
			}
		}

		if (!room.teamId) {
			throw new Error('room-not-on-team');
		}
		room.teamDefault = isDefault;
		await Rooms.setTeamDefaultById(rid, isDefault);

		if (isDefault) {
			const maxNumberOfAutoJoinMembers = settings.get<number>('API_User_Limit');
			const teamMembers = await this.members(
				uid,
				room.teamId,
				true,
				{ offset: 0, count: maxNumberOfAutoJoinMembers },
				// We should not get the owner of the room, since he is already a member
				{ _id: { $ne: room.u._id } },
			);

			for await (const m of teamMembers.records) {
				if (await addUserToRoom(room._id, m.user, user)) {
					room.usersCount++;

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Confirm the room is still on the team before updating: check room.teamId equals your team's _id
  2. Re-fetch the team's channel list (GET teams.listRooms) and use a current roomId
  3. If the room legitimately left the team, drop the update — there is nothing to toggle; treat this error as an idempotency signal in retry logic

Example fix

// before
await Team.updateRoom(uid, rid, true); // throws room-not-on-team after removal

// after
const room = await Rooms.findOneById(rid, { projection: { teamId: 1 } });
if (!room?.teamId) {
	return API.v1.failure('room-not-on-team'); // already detached — nothing to update
}
await Team.updateRoom(uid, rid, true);
Defensive patterns

Strategy: validation

Validate before calling

const room = await Rooms.findOneById(rid, { projection: { teamId: 1 } });
if (!room?.teamId) {
	throw new Error('room-not-on-team'); // nothing to update
}
await Team.updateRoom(uid, rid, isDefault, canUpdateAny);

Type guard

const isTeamRoom = (room: IRoom | null): room is IRoom & { teamId: string } =>
	!!room && typeof room.teamId === 'string' && room.teamId.length > 0;

Try / catch

try {
	await Team.updateRoom(uid, rid, isDefault);
} catch (e) {
	if (e instanceof Error && e.message === 'room-not-on-team') {
		// room already detached — idempotent success or refresh UI
	}
	throw e;
}

Prevention

When it happens

Trigger: POST /api/v1/teams.updateRoom for a channel that was already removed from its team (teams.removeRoom) or converted; calling updateRoom on a plain channel/private group that was never part of a team; a race where the room is detached from the team (unsetTeamId) between the endpoint's team lookup and the service call.

Common situations: Stale UI showing a channel under a team after it was removed elsewhere; scripts replaying old room lists; double-processing of the same request after the first already removed the room from the team.

Related errors


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