{"record":{"id":"9158d10d1dbb8bd5","repo":"RocketChat/Rocket.Chat","slug":"the-start-query-parameter-must-be-before-the-en","errorCode":null,"errorMessage":"The \"start\" query parameter must be before the \"end\" query parameter.","messagePattern":"The \"start\" query parameter must be before the \"end\" query parameter\\.","errorType":"exception","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/ee/server/api/v1/omnichannel/reports.ts","lineNumber":29,"sourceCode":"} from './lib/dashboards';\nimport { API } from '../../../../../server/api';\nimport { restrictQuery } from '../../../lib/omnichannel/restrictQuery';\n\nconst checkDates = (start: Moment, end: Moment) => {\n\tif (!start.isValid()) {\n\t\tthrow new Error('The \"start\" query parameter must be a valid date.');\n\t}\n\tif (!end.isValid()) {\n\t\tthrow new Error('The \"end\" query parameter must be a valid date.');\n\t}\n\t// Check dates are no more than 1 year apart using moment\n\t// 1.01 === \"we allow to pass year by some hours/days\"\n\tif (moment(end).startOf('day').diff(moment(start).startOf('day'), 'year', true) > 1.01) {\n\t\tthrow new Error('The \"start\" and \"end\" query parameters must be less than 1 year apart.');\n\t}\n\n\tif (start.isAfter(end)) {\n\t\tthrow new Error('The \"start\" query parameter must be before the \"end\" query parameter.');\n\t}\n};\n\nAPI.v1.addRoute(\n\t'livechat/analytics/dashboards/conversations-by-source',\n\t{\n\t\tauthRequired: true,\n\t\tpermissionsRequired: ['view-livechat-reports'],\n\t\tvalidateParams: isGETDashboardConversationsByType,\n\t\tlicense: ['livechat-enterprise'],\n\t},\n\t{\n\t\tasync get() {\n\t\t\tconst { start, end } = this.queryParams;\n\n\t\t\tconst startDate = moment(start);\n\t\t\tconst endDate = moment(end);\n","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/api/v1/omnichannel/reports.ts#L11-L47","documentation":"checkDates requires start not to be after end (equal dates pass; only strictly-after fails). This is the last date check, running after validity and the 1-year span check, on every livechat/analytics/dashboards/* report route.","triggerScenarios":"GET .../conversations-by-source?start=2024-06-30&end=2024-01-01, or a client sending {from, to} in the wrong order after a refactor.","commonSituations":"Swapped query params; timezone conversion pushing a client's 'today' start past the server-parsed end; date pickers defaulting start to tomorrow; end truncated to midnight while start keeps a late-evening time.","solutions":["Order the pair before sending: send min(start,end) as start and max(start,end) as end.","Check the UI binding for swapped from/to fields.","Log the exact query string when dashboard requests fail to catch silent swaps."],"exampleFix":"// before\napi.get(url, { params: { start, end } }); // user picked end before start\n\n// after\nconst [s, e] = start.isAfter(end) ? [end, start] : [start, end];\napi.get(url, { params: { start: s.toISOString(), end: e.toISOString() } });","handlingStrategy":"validation","validationCode":"const [s, e] = start.isAfter(end) ? [end, start] : [start, end];\nawait api.get(url, { params: { start: s.toISOString(), end: e.toISOString() } });","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Sort the pair before sending; only strictly-after fails, equal dates are fine.","Beware timezone conversions that push start past end at day boundaries.","Log the final query string to catch swapped params in UI refactors."],"tags":["omnichannel","analytics","date-range","rest-api"],"backgroundTag":"invalid-date-range","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"}