RocketChat/Rocket.Chat · error · Meteor.Error

error-fromTs-requires-lastUpdate

error-fromTs-requires-lastUpdate

Error message

The "fromTs" parameter can only be used together with "lastUpdate"

What it means

Thrown by getMessageHistory when 'fromTs' is passed without 'lastUpdate'. 'fromTs' only bounds the query on the lastUpdate sync path; cursor pagination and the plain channel-history fallback ignore it, so accepting it there would silently widen the result set. The server therefore rejects the combination instead of returning more data than requested.

Source

Thrown at apps/meteor/server/publications/messages.ts:266

	if ((next || previous) && !type) {
		throw new Meteor.Error('error-type-param-required', 'The "type" parameter is required when using the "next" or "previous" parameters');
	}

	if (next && previous) {
		throw new Meteor.Error('error-cursor-conflict', 'You cannot provide both "next" and "previous" parameters');
	}

	if ((next || previous) && lastUpdate) {
		throw new Meteor.Error(
			'error-cursor-and-lastUpdate-conflict',
			'The attributes "next", "previous" and "lastUpdate" cannot be used together',
		);
	}

	// `fromTs` only bounds the query on the `lastUpdate` path; neither cursor pagination nor the channel
	// history fallback honors it, so accepting it there would silently widen the result set.
	if (fromTs && !lastUpdate) {
		throw new Meteor.Error('error-fromTs-requires-lastUpdate', 'The "fromTs" parameter can only be used together with "lastUpdate"');
	}

	const hasCursorPagination = !!((next || previous) && count !== null && type);

	if (!hasCursorPagination && !lastUpdate) {
		return getChannelHistory({ rid, fromUserId: fromId, latest: latestDate, oldest: oldestDate, inclusive, count, unreads });
	}

	if (lastUpdate) {
		return handleWithoutPagination(rid, lastUpdate, fromTs);
	}

	if (!type) {
		throw new Meteor.Error('error-param-required', 'The "type" or "lastUpdate" parameters must be provided');
	}

	return handleCursorPagination(type, rid, count, next, previous);
};

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Add 'lastUpdate' to the same payload: fromTs is only honored as an additional lower bound on that path
  2. Drop 'fromTs' and use 'oldestDate' for plain channel-history bounding
  3. For cursor pagination, rely on the cursor token instead of fromTs

Example fix

// before
Meteor.call('messages/get', rid, { fromTs });

// after
Meteor.call('messages/get', rid, { lastUpdate, fromTs });
Defensive patterns

Strategy: validation

Validate before calling

if (params.fromTs && !params.lastUpdate) {
  throw new Error('fromTs requires lastUpdate');
}
Meteor.call('messages/get', rid, params);

Type guard

function isSyncParams(p: { fromTs?: Date; lastUpdate?: Date }): p is { fromTs: Date; lastUpdate: Date } {
  return Boolean(p.lastUpdate && (!p.fromTs || p.fromTs instanceof Date));
}

Try / catch

try { await Meteor.callAsync('messages/get', rid, params); } catch (e) { if (e.error === 'error-fromTs-requires-lastUpdate') { delete params.fromTs; /* retry or surface */ } }

Prevention

When it happens

Trigger: Meteor.call('messages/get', rid, { fromTs: new Date('2024-01-01') }) without lastUpdate; passing fromTs together with next/previous cursor pagination; forwarding a fromTs filter into the plain history fallback.

Common situations: A client adds a 'fetch messages since timestamp' filter assuming it applies to all modes; REST bridges mapping a 'since' query param to fromTs without mapping the corresponding lastUpdate field.

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/3c3944421d05e3e3. Report an issue: GitHub.