{"record":{"id":"537140b18f4d83d4","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-date-537140","errorCode":"error-invalid-date","errorMessage":"Invalid date","messagePattern":"Invalid date","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/messages/getChannelHistory.ts","lineNumber":74,"sourceCode":"\tconst room = await Rooms.findOneById(rid);\n\tif (!room) {\n\t\treturn false;\n\t}\n\n\t// Make sure they can access the room\n\tif (!(await Authorization.canReadRoom(room, { _id: fromUserId }))) {\n\t\treturn false;\n\t}\n\n\t// Ensure latest is always defined.\n\tif (latest === undefined) {\n\t\tlatest = new Date();\n\t}\n\n\t// Verify oldest is a date if it exists\n\n\tif (oldest !== undefined && {}.toString.call(oldest) !== '[object Date]') {\n\t\tthrow new Meteor.Error('error-invalid-date', 'Invalid date', { method: 'getChannelHistory' });\n\t}\n\n\tconst hiddenSystemMessages = settings.get<MessageTypesValues[]>('Hide_System_Messages');\n\n\tconst hiddenMessageTypes = getHiddenSystemMessages(room, hiddenSystemMessages);\n\n\tconst options: Record<string, unknown> = {\n\t\tsort: {\n\t\t\tts: -1,\n\t\t},\n\t\tskip: offset,\n\t\tlimit: count,\n\t};\n\n\tconst records =\n\t\toldest === undefined\n\t\t\t? await Messages.findVisibleByRoomIdBeforeTimestampNotContainingTypes(\n\t\t\t\t\trid,","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/messages/getChannelHistory.ts#L56-L92","documentation":"getChannelHistory validates that oldest, when provided, is a real Date instance using {}.toString.call(oldest) !== '[object Date]', and throws 'error-invalid-date' otherwise. DDP method params must carry actual Date objects (EJSON preserves them across the wire); an ISO string like '2024-01-01T00:00:00.000Z' or an epoch number fails this check even though it looks date-like.","triggerScenarios":"Meteor.call('getChannelHistory', { rid, oldest: '2024-01-01T00:00:00.000Z' }) — passing an ISO string or a timestamp number; params serialized with plain JSON.stringify instead of EJSON; raw form/URL inputs fed to the method.","commonSituations":"Porting code from the REST API (channels.history accepts ISO date strings) to the DDP method; wrappers that round-trip params through plain JSON; date pickers returning strings.","solutions":["Pass Date objects: Meteor.call('getChannelHistory', { rid, oldest: new Date('2024-01-01T00:00:00.000Z') })","If your API accepts strings, convert before invoking: oldest = new Date(oldest)","Or use GET /v1/channels.history, whose date params are ISO strings parsed server-side (also the deprecation target)"],"exampleFix":"// before\nMeteor.call('getChannelHistory', { rid, oldest: '2024-01-01T00:00:00.000Z' });\n\n// after\nMeteor.call('getChannelHistory', { rid, oldest: new Date('2024-01-01T00:00:00.000Z') });","handlingStrategy":"type-guard","validationCode":"const oldestDate = typeof oldest === 'string' ? new Date(oldest) : oldest;\nif (Number.isNaN(oldestDate?.getTime?.())) {\n  throw new TypeError('oldest must be a parseable date');\n}\nMeteor.call('getChannelHistory', { rid, oldest: oldestDate });","typeGuard":"function isDate(value: unknown): value is Date {\n  return {}.toString.call(value) === '[object Date]';\n}\n// usage: if (!isDate(oldest)) oldest = new Date(oldest);","tryCatchPattern":null,"preventionTips":["Never pass raw strings/numbers as date params to DDP methods — EJSON only preserves real Date objects","Serialize method params with EJSON, not JSON.stringify, when round-tripping them","Centralize date coercion at the boundary where user/URL input enters"],"tags":["meteor","ddp","date-validation","ejson"],"backgroundTag":"invalid-date-parameter","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"}