{"record":{"id":"9a161907e4fd3b30","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-dates","errorCode":"error-invalid-dates","errorMessage":"From date cannot be after To date","messagePattern":"From date cannot be after To date","errorType":"exception","errorClass":"Meteor.Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/v1/rooms.ts","lineNumber":1035,"sourceCode":"\t\t\tthrow new Meteor.Error('error-invalid-room');\n\t\t}\n\n\t\tconst user = await Users.findOneById(this.userId);\n\n\t\tif (!user || !(await canAccessRoomAsync(room, user))) {\n\t\t\tthrow new Meteor.Error('error-not-allowed', 'Not Allowed');\n\t\t}\n\n\t\tif (type === 'file') {\n\t\t\tconst { dateFrom, dateTo } = this.bodyParams;\n\t\t\tconst { format } = this.bodyParams;\n\n\t\t\tconst convertedDateFrom = dateFrom ? new Date(dateFrom) : new Date(0);\n\t\t\tconst convertedDateTo = dateTo ? new Date(dateTo) : new Date();\n\t\t\tconvertedDateTo.setDate(convertedDateTo.getDate() + 1);\n\n\t\t\tif (convertedDateFrom > convertedDateTo) {\n\t\t\t\tthrow new Meteor.Error('error-invalid-dates', 'From date cannot be after To date');\n\t\t\t}\n\n\t\t\tvoid dataExport.sendFile(\n\t\t\t\t{\n\t\t\t\t\trid,\n\t\t\t\t\tformat,\n\t\t\t\t\tdateFrom: convertedDateFrom,\n\t\t\t\t\tdateTo: convertedDateTo,\n\t\t\t\t},\n\t\t\t\tuser,\n\t\t\t);\n\t\t\treturn API.v1.success();\n\t\t}\n\n\t\tif (type === 'email') {\n\t\t\tconst { toUsers, toEmails, subject, messages } = this.bodyParams;\n\n\t\t\tif ((!toUsers || toUsers.length === 0) && (!toEmails || toEmails.length === 0)) {","sourceCodeStart":1017,"sourceCodeEnd":1053,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/server/api/v1/rooms.ts#L1017-L1053","documentation":"Thrown by POST rooms.mail (type 'file') when the user-supplied dateFrom is later than dateTo (after dateTo is bumped by one day internally). The server builds a date window for the export and rejects inverted ranges.","triggerScenarios":"POST /api/v1/rooms.mail with { type:'file', dateFrom, dateTo } where new Date(dateFrom) > new Date(dateTo). Note dateTo gets +1 day before the comparison, so only ranges off by more than a day trip it.","commonSituations":"UI date pickers letting the user pick a 'from' after 'to'; timezone confusion making the same calendar day compare as inverted; swapped form fields; default values that don't make sense.","solutions":["Enforce dateFrom <= dateTo in the picker and disable the submit button otherwise.","Swap or normalize the two values if inverted before sending.","Send both as ISO-8601 UTC to avoid timezone-driven inversions."],"exampleFix":"// before\nawait rest.post('/api/v1/rooms.mail', { rid, type:'file', dateFrom, dateTo });\n\n// after\nif (new Date(dateFrom) > new Date(dateTo)) [dateFrom, dateTo] = [dateTo, dateFrom];\nawait rest.post('/api/v1/rooms.mail', {\n  rid, type:'file',\n  dateFrom: new Date(dateFrom).toISOString(),\n  dateTo: new Date(dateTo).toISOString(),\n});","handlingStrategy":"validation","validationCode":"const from = new Date(dateFrom).getTime();\nconst to = new Date(dateTo).getTime();\nif (isNaN(from) || isNaN(to)) throw new Error('invalid dates');\nif (from > to) [dateFrom, dateTo] = [dateTo, dateFrom];","typeGuard":"function isValidDateRange(a: unknown, b: unknown): boolean {\n  const f = Date.parse(String(a)); const t = Date.parse(String(b));\n  return !isNaN(f) && !isNaN(t) && f <= t;\n}","tryCatchPattern":"try {\n  await rest.post(url, { rid, type:'file', dateFrom, dateTo });\n} catch (e) {\n  if (isMeteorError(e, 'error-invalid-dates')) {\n    // swap and retry\n  } else throw e;\n}","preventionTips":["Lock the date picker so 'from' <= 'to'.","Send ISO-8601 UTC to avoid timezone inversions.","Normalize swapped values before submit."],"tags":["validation","date-range","mail","rest-api"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}