hcengineering/platform · warning

Modifications of reaction type are not allowed.

Error message

Modifications of reaction type are not allowed.

What it means

validateReactionUpdate inspects TxUpdateDoc for DocReaction and rejects any operation attempting to change the reactionType field, since reactions are immutable in kind after creation (change = remove + create a new reaction). Only other permitted fields may be updated.

Source

Thrown at server-plugins/rating/src/index.ts:98

    const current = await this.provideFindAll(ctx, rating.class.DocReaction, {
      attachedTo: create.attachedTo,
      attachedToClass: create.objectClass
    })
    if (
      current.some(
        (it) =>
          it.reactionType === create.attributes.reactionType &&
          (it.value === create.attributes.value ||
            (create.attributes.reactionType === ReactionKind.Emoji && it.emoji === create.attributes.emoji))
      )
    ) {
      throw new Error('Duplicate emoji reaction is not allowed.')
    }
  }

  private async validateReactionUpdate (ctx: MeasureContext, upd: TxUpdateDoc<DocReaction>): Promise<void> {
    if (upd.operations.reactionType !== undefined) {
      throw new Error('Modifications of reaction type are not allowed.')
    }
    // Find current reaction tried to be modified
    const current = (
      await this.provideFindAll(ctx, rating.class.DocReaction, {
        _id: upd.objectId,
        _class: upd.objectClass
      })
    ).shift()
    if (current === undefined) {
      throw new Error('Reaction not found.')
    }
    if (upd.operations.value === undefined || upd.operations.value < 0 || upd.operations.value > 10) {
      throw new Error('Reaction value modification is required.')
    }
  }

  private async validateReactionRemove (ctx: MeasureContext, upd: TxRemoveDoc<DocReaction>): Promise<void> {
    // Find current reaction tried to be modified

View on GitHub (pinned to 63e28dc964)

Solutions

  1. To change a reaction type, delete the existing DocReaction and create a new one with the desired type.
  2. Update the client to replace update-with-reactionType by remove+create flow.
  3. Remove reactionType from any migration/bulk update operation sets on DocReaction.

Example fix

// before
await client.updateDoc(rating.class.DocReaction, _id, { reactionType: ReactionKind.Emoji })
// after
await client.removeDoc(rating.class.DocReaction, space, _id)
await client.createDoc(rating.class.DocReaction, space, {
  attachedTo: docId,
  reactionType: ReactionKind.Emoji,
  emoji: '👍'
})
Defensive patterns

Strategy: validation

Validate before calling

const ops: Record<string, any> = { ...update.operations }
if ('reactionType' in ops) {
  throw new Error('Remove and re-create the reaction to change its type')
}

Type guard

function isReactionTypeUpdate(upd: TxUpdateDoc<DocReaction>): boolean {
  return upd.operations.reactionType !== undefined
}

Try / catch

try {
  await client.updateDoc(rating.class.DocReaction, _id, ops)
} catch (err) {
  if (/Modifications of reaction type/.test(err.message)) {
    await client.removeDoc(rating.class.DocReaction, space, _id)
    await client.createDoc(rating.class.DocReaction, space, newReaction)
    return
  }
  throw err
}

Prevention

When it happens

Trigger: Submitting an update transaction on a DocReaction whose operations include reactionType — e.g. UI letting a user switch a Like to an Emoji in place, or migration code patching reactionType on existing rows.

Common situations: Frontend reaction picker updating the same doc instead of deleting and re-creating, bulk scripts normalizing reaction types, schema migrations adjusting reactionType values.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/4524b001b3261d7d. Report an issue: GitHub.