{"record":{"id":"36739c10dc4b05ec","repo":"RocketChat/Rocket.Chat","slug":"error-pinning-message","errorCode":"error-pinning-message","errorMessage":"Error pinning message","messagePattern":"Error pinning message","errorType":"exception","errorClass":"PinMessagesNotAllowed","httpStatus":null,"severity":"error","filePath":"apps/meteor/client/lib/mutationEffects/updatePinMessage.ts","lineNumber":10,"sourceCode":"import type { IMessage } from '@rocket.chat/core-typings';\n\nimport { Messages } from '../../stores';\nimport { PinMessagesNotAllowed } from '../errors/PinMessagesNotAllowed';\n\nexport const updatePinMessage = (message: IMessage, data: Partial<IMessage>) => {\n\tconst msg = Messages.state.get(message._id);\n\n\tif (!msg) {\n\t\tthrow new PinMessagesNotAllowed('Error pinning message', {\n\t\t\tmethod: 'pinMessage',\n\t\t});\n\t}\n\n\tMessages.state.update(\n\t\t(record) => record._id === message._id && record.rid === message.rid,\n\t\t(record) => ({ ...record, ...data }),\n\t);\n};\n","sourceCodeStart":1,"sourceCodeEnd":20,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/client/lib/mutationEffects/updatePinMessage.ts#L1-L20","documentation":"updatePinMessage() is the optimistic-update hook for the pinMessage mutation: it throws PinMessagesNotAllowed('Error pinning message', { method: 'pinMessage' }) — error code error-pinning-message — when Messages.state.get(message._id) is undefined, i.e. the message being pinned is not in the local store and there is nothing to optimistically mark as pinned.","triggerScenarios":"The pinMessage mutation firing for a message that was never stored in Messages.state (pinned from a surface that fetched the message via REST without hydrating the store) or that was evicted/removed from the cache before the mutation ran.","commonSituations":"Pin actions in search results, pinned-messages lists, or notification contexts that bypass the room message cache; cache clears mid-session; double mutations after message deletion.","solutions":["Ensure the message is stored (Messages.state.store(message)) before dispatching the pin mutation.","Catch PinMessagesNotAllowed specifically and re-fetch/refresh instead of leaving the UI stuck.","Route pin actions through the normal room data layer so the message is always cached."],"exampleFix":"// before\nupdatePinMessage(message, { pinned: true }); // throws if not in store\n\n// after\nif (!Messages.state.get(message._id)) {\n  Messages.state.store(message); // hydrate before optimistic update\n}\nupdatePinMessage(message, { pinned: true });","handlingStrategy":"try-catch","validationCode":"if (!Messages.state.get(message._id)) {\n  Messages.state.store(message); // hydrate before the optimistic update\n}\nupdatePinMessage(message, { pinned: true });","typeGuard":"const isMessageCached = (message: IMessage): boolean => Boolean(Messages.state.get(message._id));","tryCatchPattern":"import { PinMessagesNotAllowed } from '../../../lib/errors/PinMessagesNotAllowed';\ntry {\n  updatePinMessage(message, { pinned: true });\n} catch (e) {\n  if (e instanceof PinMessagesNotAllowed) {\n    dispatchToastMessage({ type: 'error', message: t('error-pinning-message') });\n    return;\n  }\n  throw e;\n}","preventionTips":["Route pin actions through surfaces whose messages are already in Messages.state.","Store the message before dispatching the pinMessage mutation.","Catch PinMessagesNotAllowed explicitly and refresh rather than leaving toggled UI."],"tags":["pinning","messages","mutation"],"backgroundTag":"pin-message-not-allowed","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}