{"record":{"id":"3a52f083ef5455b3","repo":"RocketChat/Rocket.Chat","slug":"invalid-date","errorCode":null,"errorMessage":"Invalid Date","messagePattern":"Invalid Date","errorType":"exception","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/publications/messages.ts","lineNumber":49,"sourceCode":"\t\t\t\t\tupdated: IMessage[];\n\t\t\t\t\tdeleted: IMessage[];\n\t\t\t\t\tcursor?: {\n\t\t\t\t\t\tnext: string | null;\n\t\t\t\t\t\tprevious: string | null;\n\t\t\t\t\t};\n\t\t\t  }\n\t\t\t| boolean\n\t\t\t| IMessage[]\n\t\t\t| { messages: IMessage[]; firstUnread?: any; unreadNotLoaded?: number }\n\t\t>;\n\t}\n}\n\nexport function extractTimestampFromCursor(cursor: string): Date {\n\tconst timestamp = parseInt(cursor, 10);\n\n\tif (isNaN(timestamp) || new Date(timestamp).toString() === 'Invalid Date') {\n\t\tthrow new Error('Invalid Date');\n\t}\n\n\treturn new Date(timestamp);\n}\n\nexport function mountCursorQuery({ next, previous, count }: { next?: string; previous?: string; count: number }): {\n\tquery: { $gt: Date } | { $lt: Date };\n\toptions: FindOptions<IMessage>;\n} {\n\tconst options: FindOptions<IMessage> = {\n\t\tsort: { _updatedAt: 1 },\n\t\t...(next || previous ? { limit: count + 1 } : {}),\n\t};\n\n\tif (next) {\n\t\treturn { query: { $gt: extractTimestampFromCursor(next) }, options };\n\t}\n","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/publications/messages.ts#L31-L67","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nMeteor.call('messages/get', rid, { type: 'UPDATED', next: new Date().toISOString() }); // throws 'Invalid Date'\n\n// after\nMeteor.call('messages/get', rid, { type: 'UPDATED', next: String(Date.now()) });","handlingStrategy":"validation","validationCode":"const isCursorTimestamp = (v: unknown): boolean =>\n  typeof v === 'string' && /^\\d{1,14}$/.test(v) && !Number.isNaN(new Date(Number(v)).getTime());\n\nif (next && !isCursorTimestamp(next)) throw new TypeError('next must be an epoch-ms string');","typeGuard":"const isCursorTimestamp = (v: unknown): v is string =>\n  typeof v === 'string' && /^\\d{1,14}$/.test(v) && !Number.isNaN(new Date(Number(v)).getTime());","tryCatchPattern":null,"preventionTips":["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"],"tags":["pagination","cursor","messages-api","date-validation"],"backgroundTag":"invalid-cursor-timestamp","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}