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
- Echo the cursor exactly as returned in the response's cursor.next / cursor.previous
- If you must synthesize one, use an integer epoch-ms string: String(Date.now())
- Validate with /^\d+$/ plus a range check before sending
- 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
- Treat cursors as opaque: forward the exact string from the previous response
- When synthesizing cursors, use String(Date.now()) — integer milliseconds, no ISO strings
- Never JSON-encode or reformat the cursor between response and next request
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
- error-cursor-not-found
- error-cursor-conflict
- error-type-param-not-supported
- error-type-param-required
- error-invalid-cursor
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/3a52f083ef5455b3.
Report an issue: GitHub.