RocketChat/Rocket.Chat · error · Meteor.Error

invalid-params

invalid-params

Error message

tshow provided but missing tmid

What it means

executeSendMessage validates thread payload shape first: if message.tshow is set but message.tmid is missing it throws invalid-params ('tshow provided but missing tmid'). tshow means 'also show this thread reply in the main channel' and is meaningless without a parent thread id, so the pair is enforced up front.

Source

Thrown at apps/meteor/server/meteor-methods/messages/sendMessage.ts:38

import { metrics } from '../../lib/metrics';
import { settings } from '../../settings';
/**
 *
 * @param uid
 * @param message
 * @param extraInfo
 *   - ts: The timestamp of the message. the message object already has a ts, but this value is validated and only a window of 10 seconds is allowed to be used. this value overrides the message.ts value without validation.
 *
 *
 * @returns
 */
export async function executeSendMessage(
	uid: IUser['_id'] | IUser,
	message: AtLeast<IMessage, 'rid'>,
	extraInfo?: { ts?: Date; previewUrls?: string[] },
) {
	if (message.tshow && !message.tmid) {
		throw new Meteor.Error('invalid-params', 'tshow provided but missing tmid', {
			method: 'sendMessage',
		});
	}

	if (message.tmid && !settings.get('Threads_enabled')) {
		throw new Meteor.Error('error-not-allowed', 'not-allowed', {
			method: 'sendMessage',
		});
	}

	const isTimestampFromClient = Boolean(!extraInfo?.ts && message.ts);
	const now = new Date();
	message.ts = extraInfo?.ts ?? message.ts ?? now;
	if (isTimestampFromClient) {
		const tsDiff = Math.abs(moment(message.ts).diff(Date.now()));
		if (tsDiff > 60000) {
			throw new Meteor.Error('error-message-ts-out-of-sync', 'Message timestamp is out of sync', {
				method: 'sendMessage',

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Only set tshow on actual thread replies where tmid is present
  2. Assign tmid before tshow when building the payload so the invariant cannot be violated
  3. For normal channel messages omit tshow entirely

Example fix

// before
const message = { rid, msg, tshow: true };
await Meteor.callAsync('sendMessage', message);

// after
const message = { rid, msg, ...(tmid ? { tmid, tshow: true } : {}) };
await Meteor.callAsync('sendMessage', message);
Defensive patterns

Strategy: validation

Validate before calling

// invariant: tshow requires tmid
const isValidThreadPayload = (m: { tmid?: string; tshow?: boolean }): boolean =>
	!m.tshow || Boolean(m.tmid);

if (!isValidThreadPayload(message)) {
	delete message.tshow;
}
await Meteor.callAsync('sendMessage', message);

Type guard

const hasValidThreadShape = (
	m: { tmid?: string; tshow?: boolean },
): m is { tmid: string; tshow?: boolean } =>
	typeof m.tmid === 'string' && m.tmid.length > 0 && (!('tshow' in m) || !m.tshow || true) && Boolean(m.tshow) === Boolean(m.tshow && m.tmid);

Try / catch

try {
	await Meteor.callAsync('sendMessage', message);
} catch (e: any) {
	if (e?.error === 'invalid-params' && e?.reason?.includes('tshow')) {
		// payload bug: rebuild with tmid set or tshow removed, then resend
	}
	throw e;
}

Prevention

When it happens

Trigger: Sending { rid, msg, tshow: true } with no tmid; client code that sets tshow first and conditionally assigns tmid later; reusing a thread-reply payload template for a normal channel message without clearing tshow.

Common situations: Copy-pasted message-building code where the tmid branch was removed but tshow stayed; UI toggle for 'show in channel' left on while the reply context was lost; refactor that renamed tmid but kept tshow.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


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