RocketChat/Rocket.Chat · error · Meteor.Error

error-room-not-found

error-room-not-found

Error message

The required "roomId" param provided does not match any direct message

What it means

Thrown by the shared findDirectMessageRoom helper when the room resolved from roomId/username is either missing or not of type 'd'. The lookup uses getRoomByNameOrIdWithOptionToJoin with type:'d', then re-checks room.t === 'd'. This single guard backs most im.* endpoints (close, delete, setTopic, counters, files, members, messages, history, blockUser), so it is the most common im API failure.

Source

Thrown at apps/meteor/server/api/v1/im.ts:68

	if (typeof nameOrId !== 'string') {
		throw new Meteor.Error('error-room-param-not-provided', 'Query param "roomId" or "username" is required');
	}

	const user = await Users.findOneById(uid);
	if (!user) {
		throw new Meteor.Error('error-invalid-user', 'Invalid user', {
			method: 'findDirectMessageRoom',
		});
	}

	const room = await getRoomByNameOrIdWithOptionToJoin({
		user,
		nameOrId,
		type: 'd',
	});

	if (!room || room?.t !== 'd') {
		throw new Meteor.Error('error-room-not-found', 'The required "roomId" param provided does not match any direct message');
	}

	const subscription = await Subscriptions.findOne({ 'rid': room._id, 'u._id': uid });

	return {
		room,
		subscription,
	};
};

type DmDeleteProps =
	| {
			roomId: string;
	  }
	| {
			username: string;
	  };

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Verify the value is a real DM rid by calling rooms.info or im.list before the operation.
  2. If resolving by username, confirm the username is correct and that a DM exists (im.create.open with that username first).
  3. Make sure you are not passing a #channel or group rid into an im.* endpoint.
  4. Refresh the roomId from im.list after a workspace event that may have removed the DM.

Example fix

// before
await POST /api/v1/im.close { roomId: 'GENERAL' } // a channel rid

// after
const dm = await POST /api/v1/im.create { username: 'bob' };
await POST /api/v1/im.close { roomId: dm.room.rid };
Defensive patterns

Strategy: validation

Validate before calling

// Resolve and type-check the DM before any mutating im.* call
async function assertDmExists(api, ridOrName) {
  const res = await api.get('/api/v1/rooms.info', { params: { roomId: ridOrName } });
  if (res.data.room.t !== 'd') {
    throw new Error(`Expected a direct message, got room type ${res.data.room.t}`);
  }
  return res.data.room; // ._id is the canonical rid
}

Type guard

function isDirectMessageRoom(room: unknown): room is { _id: string; t: 'd' } {
  return typeof room === 'object' && room !== null
    && (room as any).t === 'd'
    && typeof (room as any)._id === 'string';
}

Try / catch

try {
  await api.post('/api/v1/im.close', { roomId });
} catch (e) {
  if (e.response?.data?.error === 'error-room-not-found') {
    // refresh rid from im.list and either retry once or give up
  } else throw e;
}

Prevention

When it happens

Trigger: Calling any im.* endpoint that delegates to findDirectMessageRoom with a roomId that does not exist, that exists but is a channel/group rather than a DM, or a username that has no DM with the calling user. Also when the DM was deleted between resolution and the call.

Common situations: Client holds a stale roomId after the DM was closed/erased; passing a channel rid into an im endpoint by mistake; username typo when resolving by username; the DM target user was deactivated and the room pruned.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/0630256c82ace490. Report an issue: GitHub.