{"record":{"id":"0595af80174fb338","repo":"hcengineering/platform","slug":"duplicate-emoji-reaction-is-not-allowed","errorCode":null,"errorMessage":"Duplicate emoji reaction is not allowed.","messagePattern":"Duplicate emoji reaction is not allowed\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server-plugins/rating/src/index.ts","lineNumber":92,"sourceCode":"    }\n    return await this.provideTx(ctx, tx)\n  }\n\n  private async validateNewReaction (ctx: MeasureContext, create: TxCreateDoc<DocReaction>): Promise<void> {\n    // Should allow only one reaction per document per user for Emoji, Like, Usefull\n    const current = await this.provideFindAll(ctx, rating.class.DocReaction, {\n      attachedTo: create.attachedTo,\n      attachedToClass: create.objectClass\n    })\n    if (\n      current.some(\n        (it) =>\n          it.reactionType === create.attributes.reactionType &&\n          (it.value === create.attributes.value ||\n            (create.attributes.reactionType === ReactionKind.Emoji && it.emoji === create.attributes.emoji))\n      )\n    ) {\n      throw new Error('Duplicate emoji reaction is not allowed.')\n    }\n  }\n\n  private async validateReactionUpdate (ctx: MeasureContext, upd: TxUpdateDoc<DocReaction>): Promise<void> {\n    if (upd.operations.reactionType !== undefined) {\n      throw new Error('Modifications of reaction type are not allowed.')\n    }\n    // Find current reaction tried to be modified\n    const current = (\n      await this.provideFindAll(ctx, rating.class.DocReaction, {\n        _id: upd.objectId,\n        _class: upd.objectClass\n      })\n    ).shift()\n    if (current === undefined) {\n      throw new Error('Reaction not found.')\n    }\n    if (upd.operations.value === undefined || upd.operations.value < 0 || upd.operations.value > 10) {","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/server-plugins/rating/src/index.ts#L74-L110","documentation":"When a DocReaction creation transaction arrives, the plugin validates that the user has not already created an identical reaction: same reactionType and same value, or for Emoji reactions the same emoji. If an equivalent existing reaction is found, the duplicate create is rejected with this error.","triggerScenarios":"Submitting TxCreateDoc for rating.class.DocReaction when the same user already has a reaction on the same attachedTo doc with the same reactionType and value/emoji — e.g. double-clicking a like button, retrying a request that actually succeeded, or replayed transactions.","commonSituations":"UI not debouncing star/like clicks, optimistic-UI retries after network timeouts, offline clients syncing queued reactions twice, event/tx replays.","solutions":["Catch this error client-side and treat it as success/idempotent (the reaction already exists).","Query existing DocReaction for (user, attachedTo, reactionType) before creating.","Debounce/disable the reaction button until the server confirms.","Make the create idempotent by checking via findAll on the plugin before submitting the tx."],"exampleFix":"// before\nawait client.createDoc(rating.class.DocReaction, space, reaction)\n// after\nconst existing = await client.findAll(rating.class.DocReaction, {\n  attachedTo: docId, createdBy: me, reactionType: reaction.reactionType\n})\nif (existing.length === 0) {\n  await client.createDoc(rating.class.DocReaction, space, reaction)\n}","handlingStrategy":"try-catch","validationCode":"const existing = await client.findAll(rating.class.DocReaction, {\n  attachedTo: docId,\n  createdBy: myAccountId,\n  reactionType,\n})\nif (existing.length > 0) return // already reacted; skip create","typeGuard":null,"tryCatchPattern":"try {\n  await client.createDoc(rating.class.DocReaction, space, reaction)\n} catch (err) {\n  if (/Duplicate emoji reaction/.test(err.message)) {\n    return // idempotent: reaction already recorded\n  }\n  throw err\n}","preventionTips":["Debounce like/emoji buttons until server confirmation.","Check for an existing identical reaction before creating.","Treat duplicate errors as success for idempotent UX.","Guard against offline sync replaying reaction creates."],"tags":["rating","reactions","duplicate","validation"],"backgroundTag":"duplicate-reaction","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}