{"record":{"id":"3c3944421d05e3e3","repo":"RocketChat/Rocket.Chat","slug":"error-fromts-requires-lastupdate","errorCode":"error-fromTs-requires-lastUpdate","errorMessage":"The \"fromTs\" parameter can only be used together with \"lastUpdate\"","messagePattern":"The \"fromTs\" parameter can only be used together with \"lastUpdate\"","errorType":"error_code","errorClass":"Meteor.Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/publications/messages.ts","lineNumber":266,"sourceCode":"\tif ((next || previous) && !type) {\n\t\tthrow new Meteor.Error('error-type-param-required', 'The \"type\" parameter is required when using the \"next\" or \"previous\" parameters');\n\t}\n\n\tif (next && previous) {\n\t\tthrow new Meteor.Error('error-cursor-conflict', 'You cannot provide both \"next\" and \"previous\" parameters');\n\t}\n\n\tif ((next || previous) && lastUpdate) {\n\t\tthrow new Meteor.Error(\n\t\t\t'error-cursor-and-lastUpdate-conflict',\n\t\t\t'The attributes \"next\", \"previous\" and \"lastUpdate\" cannot be used together',\n\t\t);\n\t}\n\n\t// `fromTs` only bounds the query on the `lastUpdate` path; neither cursor pagination nor the channel\n\t// history fallback honors it, so accepting it there would silently widen the result set.\n\tif (fromTs && !lastUpdate) {\n\t\tthrow new Meteor.Error('error-fromTs-requires-lastUpdate', 'The \"fromTs\" parameter can only be used together with \"lastUpdate\"');\n\t}\n\n\tconst hasCursorPagination = !!((next || previous) && count !== null && type);\n\n\tif (!hasCursorPagination && !lastUpdate) {\n\t\treturn getChannelHistory({ rid, fromUserId: fromId, latest: latestDate, oldest: oldestDate, inclusive, count, unreads });\n\t}\n\n\tif (lastUpdate) {\n\t\treturn handleWithoutPagination(rid, lastUpdate, fromTs);\n\t}\n\n\tif (!type) {\n\t\tthrow new Meteor.Error('error-param-required', 'The \"type\" or \"lastUpdate\" parameters must be provided');\n\t}\n\n\treturn handleCursorPagination(type, rid, count, next, previous);\n};","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/publications/messages.ts#L248-L284","documentation":"Thrown by getMessageHistory when 'fromTs' is passed without 'lastUpdate'. 'fromTs' only bounds the query on the lastUpdate sync path; cursor pagination and the plain channel-history fallback ignore it, so accepting it there would silently widen the result set. The server therefore rejects the combination instead of returning more data than requested.","triggerScenarios":"Meteor.call('messages/get', rid, { fromTs: new Date('2024-01-01') }) without lastUpdate; passing fromTs together with next/previous cursor pagination; forwarding a fromTs filter into the plain history fallback.","commonSituations":"A client adds a 'fetch messages since timestamp' filter assuming it applies to all modes; REST bridges mapping a 'since' query param to fromTs without mapping the corresponding lastUpdate field.","solutions":["Add 'lastUpdate' to the same payload: fromTs is only honored as an additional lower bound on that path","Drop 'fromTs' and use 'oldestDate' for plain channel-history bounding","For cursor pagination, rely on the cursor token instead of fromTs"],"exampleFix":"// before\nMeteor.call('messages/get', rid, { fromTs });\n\n// after\nMeteor.call('messages/get', rid, { lastUpdate, fromTs });","handlingStrategy":"validation","validationCode":"if (params.fromTs && !params.lastUpdate) {\n  throw new Error('fromTs requires lastUpdate');\n}\nMeteor.call('messages/get', rid, params);","typeGuard":"function isSyncParams(p: { fromTs?: Date; lastUpdate?: Date }): p is { fromTs: Date; lastUpdate: Date } {\n  return Boolean(p.lastUpdate && (!p.fromTs || p.fromTs instanceof Date));\n}","tryCatchPattern":"try { await Meteor.callAsync('messages/get', rid, params); } catch (e) { if (e.error === 'error-fromTs-requires-lastUpdate') { delete params.fromTs; /* retry or surface */ } }","preventionTips":["Document that fromTs is a sub-filter of the lastUpdate sync mode only","Use oldestDate for plain-history time bounds"],"tags":["meteor","messages-get","parameter-validation","rocket-chat"],"backgroundTag":"missing-required-parameter","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T10:36:37.832Z"}