{"record":{"id":"b7ec41932da22efc","repo":"RocketChat/Rocket.Chat","slug":"error-cursor-not-found","errorCode":"error-cursor-not-found","errorMessage":"Cursor not found","messagePattern":"Cursor not found","errorType":"error_code","errorClass":"Meteor.Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/publications/messages.ts","lineNumber":84,"sourceCode":"\t}\n\n\tif (previous) {\n\t\treturn { query: { $lt: extractTimestampFromCursor(previous) }, options: { ...options, sort: { _updatedAt: -1 } } };\n\t}\n\n\treturn { query: { $gt: new Date(0) }, options };\n}\n\nexport function mountCursorFromMessage(message: IMessage & { _deletedAt?: Date }, type: 'UPDATED' | 'DELETED'): string {\n\tif (type === 'UPDATED' && message._updatedAt) {\n\t\treturn `${message._updatedAt.getTime()}`;\n\t}\n\n\tif (type === 'DELETED' && message._deletedAt) {\n\t\treturn `${message._deletedAt.getTime()}`;\n\t}\n\n\tthrow new Meteor.Error('error-cursor-not-found', 'Cursor not found', { method: 'messages/get' });\n}\n\nexport function mountNextCursor(\n\tmessages: IMessage[],\n\tcount: number,\n\ttype: CursorPaginationType,\n\tnext?: string,\n\tprevious?: string,\n): string | null {\n\tif (messages.length === 0) {\n\t\treturn null;\n\t}\n\n\tif (previous) {\n\t\treturn mountCursorFromMessage(messages[0], type);\n\t}\n\n\tif (messages.length <= count && next) {","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/publications/messages.ts#L66-L102","documentation":"mountCursorFromMessage derives the next/previous cursor from a boundary message of the fetched page: _updatedAt for type UPDATED, _deletedAt for DELETED (trash collection). If the boundary message lacks the required timestamp, the server cannot continue pagination and throws Meteor.Error 'error-cursor-not-found'. This is a server-side data-integrity failure, not a client-input error.","triggerScenarios":"A message document in the result page without _updatedAt, or a trashed document without _deletedAt — legacy documents predating the field, manual database edits, or a partial write that skipped the audit timestamp.","commonSituations":"Old installations after upgrades; imported data missing audit timestamps; direct Mongo modifications that dropped fields.","solutions":["Identify the offending documents (messages lacking _updatedAt, trash docs lacking _deletedAt) and backfill the missing timestamps","Page past the broken boundary by re-fetching without a cursor (lastUpdate mode)","Report the corrupted range upstream — these fields are set on every normal write, so their absence indicates abnormal data"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await getMessageHistory(rid, fromId, { type, next });\n} catch (error) {\n  if (error instanceof Meteor.Error && error.error === 'error-cursor-not-found') {\n    // boundary message is missing its timestamp; fall back to lastUpdate-based sync\n    return await getMessageHistory(rid, fromId, { lastUpdate: new Date(Date.now() - 60_000) });\n  }\n  throw error;\n}","preventionTips":["Never write message documents without _updatedAt (or trash docs without _deletedAt) in migrations and imports","Audit imported data for missing audit timestamps before enabling cursor pagination","Backfill missing timestamps on legacy documents instead of suppressing the error"],"tags":["pagination","cursor","data-integrity","messages-api"],"backgroundTag":"missing-message-timestamp","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"}