RocketChat/Rocket.Chat · error · Meteor.Error

error-roomid-param-not-provided

error-roomid-param-not-provided

Error message

The parameter "roomId" is required

What it means

GET /api/v1/im.messages.others requires the roomId query parameter. The action destructures this.queryParams and throws error-roomid-param-not-provided (apps/meteor/server/api/v1/im.ts:799) when roomId is missing or an empty string. This runs after the endpoint-enabled check but before any database access, so it fails fast with no side effects.

Solutions

  1. Append ?roomId=<dmRoomId> where the id is a direct-message room (t === 'd')
  2. Fetch valid ids first via GET /api/v1/im.list and pick rooms with t: 'd'
  3. Check exact casing and location: query parameter roomId, not roomid, rid, or a body field

Example fix

# before
 curl -H "X-Auth-Token: $TOKEN" -H "X-User-Id: $UID" \
  "https://chat.example.com/api/v1/im.messages.others"
 # => 400 error-roomid-param-not-provided

 # after
 curl -H "X-Auth-Token: $TOKEN" -H "X-User-Id: $UID" \
  "https://chat.example.com/api/v1/im.messages.others?roomId=AbCdEf123"
Defensive patterns

Strategy: validation

Validate before calling

function requireRoomId(params: Record<string, unknown>): string {
  const roomId = params.roomId;
  if (typeof roomId !== 'string' || roomId.trim() === '') {
    throw new Error('roomId query parameter is required for im.messages.others');
  }
  return roomId;
}
const roomId = requireRoomId({ roomId }); // pass exactly this key

Type guard

const hasRoomId = (q: Record<string, unknown>): q is { roomId: string } =>
  typeof q.roomId === 'string' && q.roomId.trim().length > 0;

Prevention

When it happens

Trigger: Calling /api/v1/im.messages.others with no query string, with roomId= (empty), or with a differently-cased key such as roomid or rid. Only exact roomId is read.

Common situations: Clients that build query strings conditionally and silently drop empty values; developers mixing conventions because other im.* POST endpoints (im.messages, im.open) take rid in the body while this GET endpoint wants roomId in the query; copy-paste from im.history examples that use a different field name.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

	response: {
		200: paginatedMessagesResponseSchema,
		400: validateBadRequestErrorResponse,
		401: validateUnauthorizedErrorResponse,
		403: validateForbiddenErrorResponse,
	},
};

const dmMessagesOthersAction = <Path extends string>(_name: Path): TypedAction<typeof dmMessagesOthersEndpointsProps, Path> =>
	async function action() {
		if (settings.get('API_Enable_Direct_Message_History_EndPoint') !== true) {
			throw new Meteor.Error('error-endpoint-disabled', 'This endpoint is disabled', {
				route: '/api/v1/im.messages.others',
			});
		}

		const { roomId } = this.queryParams;
		if (!roomId) {
			throw new Meteor.Error('error-roomid-param-not-provided', 'The parameter "roomId" is required');
		}

		const room = await Rooms.findOneById<Pick<IRoom, '_id' | 't'>>(roomId, { projection: { _id: 1, t: 1 } });
		if (!room || room?.t !== 'd') {
			throw new Meteor.Error('error-room-not-found', `No direct message room found by the id of: ${roomId}`);
		}

		const { offset, count } = await getPaginationItems(this.queryParams);
		const { sort, fields, query } = await this.parseJsonQuery();
		const ourQuery = Object.assign({}, query, { rid: room._id });

		const { cursor, totalCount } = Messages.findPaginated<IMessage>(ourQuery, {
			sort: sort || { ts: -1 },
			skip: offset,
			limit: count,
			projection: fields,
		});

View on GitHub (pinned to e4b8178b20)