{"record":{"id":"0bd5f8b38ea5921e","repo":"RocketChat/Rocket.Chat","slug":"error-cursor-conflict-0bd5f8","errorCode":"error-cursor-conflict","errorMessage":"error-cursor-conflict","messagePattern":"error-cursor-conflict","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/messages/loadRoomHistory.ts","lineNumber":66,"sourceCode":"export async function loadRoomHistory({\n\tuserId,\n\tnext,\n\tprevious,\n\tlastSeen,\n\tcount = 20,\n\tshowThreadMessages = true,\n\troom,\n}: {\n\tuserId?: string;\n\tnext?: string;\n\tprevious?: string;\n\tlastSeen?: Date;\n\tcount?: number;\n\tshowThreadMessages?: boolean;\n\troom: IRoom;\n}): Promise<RoomHistoryResult> {\n\tif (next && previous) {\n\t\tthrow new Error('error-cursor-conflict');\n\t}\n\n\tconst rid = room._id;\n\n\tconst hiddenMessageTypes = getHiddenSystemMessages(room, settings.get<MessageTypesValues[]>('Hide_System_Messages'));\n\n\t// One extra document reveals whether a further page exists.\n\tconst options: FindOptions<IMessage> = { sort: { ts: next ? 1 : -1 }, limit: count + 1 };\n\n\tconst records = next\n\t\t? await Messages.findVisibleByRoomIdBetweenTimestampsNotContainingTypes(\n\t\t\t\trid,\n\t\t\t\tdecodeHistoryCursor(next),\n\t\t\t\tFAR_FUTURE,\n\t\t\t\thiddenMessageTypes,\n\t\t\t\toptions,\n\t\t\t\tshowThreadMessages,\n\t\t\t).toArray()","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b263243745917337314259cf987c0e989cf0cdc9/apps/meteor/server/lib/messages/loadRoomHistory.ts#L48-L84","documentation":"loadRoomHistory throws error-cursor-conflict when both next and previous boolean flags are set to true simultaneously. The API supports paginating in one direction at a time; next fetches newer messages and previous fetches older messages relative to the latest/oldest cursor. Requesting both directions at once is ambiguous and rejected.","triggerScenarios":"Calling loadRoomHistory with { latest, next: true, previous: true } (or the REST equivalent with both direction params truthy), typically when a UI toggles both direction switches or when defaults merge with explicit params.","commonSituations":"Spread-merging pagination option objects where a stale next:true combines with a new previous:true; UI code that sets both flags 'to be safe'; refactoring a component that previously used a single direction enum into two booleans.","solutions":["Set only one of next or previous to true per call","Refactor the caller to derive next/previous from a single direction variable, e.g. next: dir === 'next', previous: dir === 'previous'","Audit option-merging code (object spreads, defaults) so a true flag isn't carried over from a previous request","If you need both newer and older messages, issue two separate calls"],"exampleFix":"// before\nloadRoomHistory({ room, latest, next: true, previous: true });\n\n// after\nconst goingNewer = direction === 'next';\nloadRoomHistory({ room, latest, next: goingNewer, previous: !goingNewer });","handlingStrategy":"type-guard","validationCode":"if (params.next && params.previous) {\n  throw new TypeError('pass next or previous, not both');\n}\nconst { next = false, previous = false } = pickOne(params);","typeGuard":"const isSingleDirection = (p: { next?: boolean; previous?: boolean }): boolean => !(p.next && p.previous);","tryCatchPattern":"try {\n  await loadRoomHistory(args);\n} catch (err) {\n  if (err instanceof Error && err.message === 'error-cursor-conflict') {\n    // retry with only one direction\n    await loadRoomHistory({ ...args, previous: false });\n  } else throw err;\n}","preventionTips":["Derive next/previous from a single direction variable","Avoid blind spreads of previous pagination options into new requests","Disable the opposing direction control in the UI when one is selected"],"tags":["meteor","pagination","conflicting-params","validation"],"backgroundTag":"conflicting-query-parameters","analyzedSha":"b263243745917337314259cf987c0e989cf0cdc9","analyzedAt":"2026-08-28T13:18:22.752Z","contentChangedAt":"2026-08-28T13:18:22.752Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}