{"record":{"id":"d9e495f3d353dc06","repo":"RocketChat/Rocket.Chat","slug":"invalid-reaction","errorCode":null,"errorMessage":"Invalid reaction","messagePattern":"Invalid reaction","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/meteor/app/apps/server/bridges/messages.ts","lineNumber":126,"sourceCode":"\t\t\tcase 'room':\n\t\t\t\tif (!username) {\n\t\t\t\t\tthrow new Error('Invalid username');\n\t\t\t\t}\n\n\t\t\t\tnotifications.notifyRoom(id, 'user-activity', username, isTyping ? ['user-typing'] : []);\n\t\t\t\treturn;\n\t\t\tdefault:\n\t\t\t\tthrow new Error('Unrecognized typing scope provided');\n\t\t}\n\t}\n\n\tprivate isValidReaction(reaction: Reaction): boolean {\n\t\treturn reaction.startsWith(':') && reaction.endsWith(':');\n\t}\n\n\tprotected async addReaction(messageId: string, userId: string, reaction: Reaction): Promise<void> {\n\t\tif (!this.isValidReaction(reaction)) {\n\t\t\tthrow new Error('Invalid reaction');\n\t\t}\n\n\t\treturn executeSetReaction(userId, reaction, messageId, true);\n\t}\n\n\tprotected async removeReaction(messageId: string, userId: string, reaction: Reaction): Promise<void> {\n\t\tif (!this.isValidReaction(reaction)) {\n\t\t\tthrow new Error('Invalid reaction');\n\t\t}\n\n\t\treturn executeSetReaction(userId, reaction, messageId, false);\n\t}\n}\n","sourceCodeStart":108,"sourceCodeEnd":140,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/app/apps/server/bridges/messages.ts#L108-L140","documentation":"Thrown by the Apps-Engine server bridge AppMessagesBridge.addReaction when an app adds a reaction whose string fails isValidReaction: it must both start and end with ':' (e.g. ':smile:'). Rocket.Chat keys reactions by colon-wrapped emoji shortnames, so the bridge rejects any other shape before calling executeSetReaction.","triggerScenarios":"An app calls the react API with 'smile' (no colons), ':smile' (missing trailing colon), a raw unicode emoji like '👍', an emoji image URL, or an empty/undefined reaction; the guard reaction.startsWith(':') && reaction.endsWith(':') fails and the Error propagates to the app.","commonSituations":"Apps building the reaction from slash-command params, settings, or user input without wrapping it in colons; porting code that used raw emoji characters; values with stray whitespace (':smile: ') that break the endsWith check.","solutions":["Pass the colon-wrapped shortname: ':smile:' instead of 'smile'","Normalize before calling: `reaction = ':' + name.trim().replace(/^:+|:+$/g, '') + ':'`","If the value comes from end users, validate it against your emoji set before calling react","Do not pass unicode emoji characters or image URLs — only shortnames"],"exampleFix":"// before\nawait app.getRocketChat().getChat().reactMessage(message, 'smile'); // throws Invalid reaction\n// after\nawait app.getRocketChat().getChat().reactMessage(message, ':smile:');","handlingStrategy":"validation","validationCode":"const toReaction = (name: string): string => `:${name.trim().replace(/^:+|:+$/g, '')}:`;\nconst reaction = toReaction('smile'); // ':smile:'\nif (reaction.length < 3) throw new Error('empty reaction name');\nawait app.getRocketChat().getChat().reactMessage(message, reaction);","typeGuard":"import type { Reaction } from '@rocket.chat/apps-engine/definition/messages';\nconst isReactionFormat = (r: string): r is Reaction =>\n  r.startsWith(':') && r.endsWith(':') && r.trim().length >= 3;\n\nif (!isReactionFormat(reaction)) reaction = toReaction(reaction);","tryCatchPattern":"try {\n  await app.getRocketChat().getChat().reactMessage(message, reaction);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Invalid reaction') {\n    // normalize once and retry with the wrapped shortname\n    await app.getRocketChat().getChat().reactMessage(message, toReaction(reaction));\n  } else {\n    throw err;\n  }\n}","preventionTips":["Centralize reaction formatting in one helper used by add and remove paths","Store/accept only shortnames in app settings and UIs, never raw unicode emoji","Trim user input before wrapping in colons","Unit-test the helper against ':smile', 'smile:', '', and '👍'"],"tags":["apps-engine","emoji","reaction","validation","messages-bridge"],"backgroundTag":"invalid-reaction-format","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"}