RocketChat/Rocket.Chat · error · Error

Cannot send system messages using 'chat.sendMessage'

Error message

Cannot send system messages using 'chat.sendMessage'

What it means

Thrown by POST chat.sendMessage (apps/meteor/server/api/v1/chat.ts:915) when MessageTypes.isSystemMessage(this.bodyParams.message) is true - i.e. the payload's message object carries a `t` field containing a known system-message type (isSystemMessage = t !== undefined && MessageTypes.includes(t), see packages/core-typings/src/IMessage/IMessage.ts:286). System messages (user-joined, message-pinned, subscription-role-added, etc.) are generated by the server itself; clients are not allowed to forge them, so the endpoint throws before executeSendMessage. This is a plain Error, not a Meteor.Error with an error-* code.

Solutions

  1. Strip the `t` field from the message payload before sending: send only { rid, msg (or blocks/attachments), alias, emoji, avatar, tmid, tshow }.
  2. If you actually need a system event to happen, trigger the underlying action (e.g. have the user join the room via their own flow) instead of forging the message.
  3. Sanitize forwarded/cloned message objects: destructure out _id, t, u, ts, edits, and server-only fields before reuse.
  4. Validate outgoing payloads client-side with a type guard that rejects any message containing `t`.

Example fix

// before
const { _id, u, ts, ...rest } = fetchedMessage;
await rc.post('/api/v1/chat.sendMessage', { message: rest }); // rest still has t: 'uj'

// after
const { _id, u, ts, t, ...sendable } = fetchedMessage;
await rc.post('/api/v1/chat.sendMessage', { message: sendable });
Defensive patterns

Strategy: type-guard

Validate before calling

const SYSTEM_TYPES = new Set(['uj', 'ul', 'ru', 'au', 'rm', 'ut', 'subscription-role-added', 'subscription-role-removed', 'room-archived', 'room-unarchived', 'message_pinned', 'rtc', 'utc']); // non-exhaustive: any registered MessageTypes value counts
const hasSystemType = (m: { t?: unknown }) => typeof m.t === 'string' && SYSTEM_TYPES.has(m.t);
if (hasSystemType(message)) throw new Error('strip message.t before chat.sendMessage');
await rc.post('/api/v1/chat.sendMessage', { message });

Type guard

const isSendableMessage = (m: Record<string, unknown>): m is { rid: string; msg?: string } =>
  typeof m.rid === 'string' && m.rid.length > 0 && m.t === undefined;

Try / catch

try {
  await rc.post('/api/v1/chat.sendMessage', { message });
} catch (e) {
  if (/Cannot send system messages/i.test(e?.error ?? '')) {
    const { t, ...clean } = message as { t?: string } & Record<string, unknown>;
    await rc.post('/api/v1/chat.sendMessage', { message: clean }); // retry once without t
  } else throw e;
}

Prevention

When it happens

Trigger: POST /api/v1/chat.sendMessage with body {"message": {"rid": "...", "t": "uj"}} (or rm, message_pinned, subscription-role-added, utc, ...). The body schema does not whitelist message.t, so any recognized system type passes validation and is rejected only here. Often hit by forwarding/cloning real message objects that were originally system messages.

Common situations: Bots echoing back message objects fetched via the API without stripping `t`; importers trying to recreate system events ('X joined') as messages; copy-pasting an IMessage from the docs that contains a t field; testing with fixtures seeded from system messages.

Related errors


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

Appendix: source

Thrown at apps/meteor/server/api/v1/chat.ts:920

			rateLimiterOptions: { numRequestsAllowed: 5, intervalTimeInMS: 1000, bypassPermissions: ['send-many-messages'] },
			body: isChatSendMessageProps,
			response: {
				200: ajv.compile<{ message: IMessage }>({
					type: 'object',
					properties: {
						message: { $ref: '#/components/schemas/IMessage' },
						success: { type: 'boolean', enum: [true] },
					},
					required: ['message', 'success'],
					additionalProperties: false,
				}),
				400: validateBadRequestErrorResponse,
				401: validateUnauthorizedErrorResponse,
			},
		},
		async function action() {
			if (MessageTypes.isSystemMessage(this.bodyParams.message)) {
				throw new Error("Cannot send system messages using 'chat.sendMessage'");
			}

			const sent = await applyAirGappedRestrictionsValidation(() =>
				executeSendMessage(this.user, this.bodyParams.message as Pick<IMessage, 'rid'>, { previewUrls: this.bodyParams.previewUrls }),
			);
			const [message] = await normalizeMessagesForUser([sent], this.userId);

			return API.v1.success({
				message,
			});
		},
	)
	.get(
		'chat.ignoreUser',
		{
			authRequired: true,
			query: isChatIgnoreUserProps,
			response: {

View on GitHub (pinned to e4b8178b20)