RocketChat/Rocket.Chat · error · Error

Invalid Date

Error message

Invalid Date

What it means

extractTimestampFromCursor validates the next/previous cursor parameters of the messages/get cursor pagination. Cursors are epoch-millisecond strings generated by mountCursorFromMessage; parseInt(cursor, 10) must yield a number that is not NaN and is inside Date's valid range, otherwise a plain 'Invalid Date' Error is thrown from mountCursorQuery before any data is fetched.

Source

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

					updated: IMessage[];
					deleted: IMessage[];
					cursor?: {
						next: string | null;
						previous: string | null;
					};
			  }
			| boolean
			| IMessage[]
			| { messages: IMessage[]; firstUnread?: any; unreadNotLoaded?: number }
		>;
	}
}

export function extractTimestampFromCursor(cursor: string): Date {
	const timestamp = parseInt(cursor, 10);

	if (isNaN(timestamp) || new Date(timestamp).toString() === 'Invalid Date') {
		throw new Error('Invalid Date');
	}

	return new Date(timestamp);
}

export function mountCursorQuery({ next, previous, count }: { next?: string; previous?: string; count: number }): {
	query: { $gt: Date } | { $lt: Date };
	options: FindOptions<IMessage>;
} {
	const options: FindOptions<IMessage> = {
		sort: { _updatedAt: 1 },
		...(next || previous ? { limit: count + 1 } : {}),
	};

	if (next) {
		return { query: { $gt: extractTimestampFromCursor(next) }, options };
	}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Echo the cursor exactly as returned in the response's cursor.next / cursor.previous
  2. If you must synthesize one, use an integer epoch-ms string: String(Date.now())
  3. Validate with /^\d+$/ plus a range check before sending
  4. Use milliseconds, not seconds, when constructing timestamps

Example fix

// before
Meteor.call('messages/get', rid, { type: 'UPDATED', next: new Date().toISOString() }); // throws 'Invalid Date'

// after
Meteor.call('messages/get', rid, { type: 'UPDATED', next: String(Date.now()) });
Defensive patterns

Strategy: validation

Validate before calling

const isCursorTimestamp = (v: unknown): boolean =>
  typeof v === 'string' && /^\d{1,14}$/.test(v) && !Number.isNaN(new Date(Number(v)).getTime());

if (next && !isCursorTimestamp(next)) throw new TypeError('next must be an epoch-ms string');

Type guard

const isCursorTimestamp = (v: unknown): v is string =>
  typeof v === 'string' && /^\d{1,14}$/.test(v) && !Number.isNaN(new Date(Number(v)).getTime());

Prevention

When it happens

Trigger: Calling messages/get with type=UPDATED|DELETED and a next/previous value that is not a plain integer epoch-ms string: ISO-8601 dates, floats, empty strings, random text, double-encoded values, or numbers beyond ±8.64e15 ms.

Common situations: Clients re-encoding or truncating the opaque cursor; building cursors by hand from Date.now() with seconds-vs-milliseconds confusion; cursors from a different API version pasted into a request.

Related errors


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