{"record":{"id":"17a6acc929b01916","repo":"RocketChat/Rocket.Chat","slug":"error-message-ts-out-of-sync","errorCode":"error-message-ts-out-of-sync","errorMessage":"Message timestamp is out of sync","messagePattern":"Message timestamp is out of sync","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/messages/sendMessage.ts","lineNumber":55,"sourceCode":"\tif (message.tshow && !message.tmid) {\n\t\tthrow new Meteor.Error('invalid-params', 'tshow provided but missing tmid', {\n\t\t\tmethod: 'sendMessage',\n\t\t});\n\t}\n\n\tif (message.tmid && !settings.get('Threads_enabled')) {\n\t\tthrow new Meteor.Error('error-not-allowed', 'not-allowed', {\n\t\t\tmethod: 'sendMessage',\n\t\t});\n\t}\n\n\tconst isTimestampFromClient = Boolean(!extraInfo?.ts && message.ts);\n\tconst now = new Date();\n\tmessage.ts = extraInfo?.ts ?? message.ts ?? now;\n\tif (isTimestampFromClient) {\n\t\tconst tsDiff = Math.abs(moment(message.ts).diff(Date.now()));\n\t\tif (tsDiff > 60000) {\n\t\t\tthrow new Meteor.Error('error-message-ts-out-of-sync', 'Message timestamp is out of sync', {\n\t\t\t\tmethod: 'sendMessage',\n\t\t\t\tmessage_ts: message.ts,\n\t\t\t\tserver_ts: new Date().getTime(),\n\t\t\t});\n\t\t}\n\t\tif (tsDiff > 10000) {\n\t\t\tmessage.ts = now;\n\t\t}\n\t}\n\n\tif (message.msg) {\n\t\tif (message.msg.length > (settings.get<number>('Message_MaxAllowedSize') ?? 0)) {\n\t\t\tthrow new Meteor.Error('error-message-size-exceeded', 'Message size exceeds Message_MaxAllowedSize', {\n\t\t\t\tmethod: 'sendMessage',\n\t\t\t});\n\t\t}\n\t}\n","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/messages/sendMessage.ts#L37-L73","documentation":"When no server-side ts override is supplied and the message includes a client timestamp, executeSendMessage compares it with server time. Differences over 10 seconds are silently replaced by 'now', but a difference beyond 60 seconds throws error-message-ts-out-of-sync (the payload carries message_ts and server_ts for diagnosis). The guard prevents backdated or future-dated messages.","triggerScenarios":"Client clock skewed by minutes: wrong system time/timezone set as local time, VM paused and resumed, mobile device drift, NTP not running; explicitly setting message.ts to a past or future date; an offline queue replaying old messages with their original timestamps.","commonSituations":"Laptop clock off by minutes after travel or sleep; docker/dev VM without time sync; client-side offline-first queue that stamps messages locally and replays them later; deliberate attempts to forge message times.","solutions":["Omit ts from the message payload and let the server stamp it","Sync the client clock / use the server time offset before sending","For legitimate server-controlled timestamps (imports, migrations) call executeSendMessage server-side with extraInfo.ts, which bypasses the client-ts check"],"exampleFix":"// before: client-stamped ts, breaks when clock drifts\nawait Meteor.callAsync('sendMessage', { rid, msg, ts: new Date() });\n\n// after: let the server set the timestamp\nawait Meteor.callAsync('sendMessage', { rid, msg });","handlingStrategy":"validation","validationCode":"// client: never send a client-stamped ts; check drift if you must\nconst driftMs = Math.abs(Date.now() - serverNow);\nif (driftMs > 60000) {\n\tawait resyncClock();\n}\nawait Meteor.callAsync('sendMessage', { rid, msg }); // no ts field","typeGuard":null,"tryCatchPattern":"try {\n\tawait Meteor.callAsync('sendMessage', message);\n} catch (e: any) {\n\tif (e?.error === 'error-message-ts-out-of-sync') {\n\t\t// resend without message.ts and let the server stamp it\n\t\tconst { ts, ...rest } = message;\n\t\tawait Meteor.callAsync('sendMessage', rest);\n\t\treturn;\n\t}\n\tthrow e;\n}","preventionTips":["Omit ts on send; the server stamps messages authoritatively","Use the server time offset for any client display that feeds back into payloads","For imports, pass ts server-side via executeSendMessage's extraInfo.ts, which is exempt from the check"],"tags":["meteor-method","messages","clock-skew","validation"],"backgroundTag":"clock-skew","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}