hcengineering/platform · error
Reaction not found.
Error message
Reaction not found.
What it means
The rating plugin validates every TxUpdateDoc for a DocReaction before applying it. validateReactionUpdate looks up the existing reaction by _id and _class via provideFindAll; if no document matches, the reaction being updated does not exist (or was already removed), so it throws 'Reaction not found.' to prevent updating a phantom document.
Source
Thrown at server-plugins/rating/src/index.ts:108
)
) {
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
// const current = (
// await this.provideFindAll(ctx, rating.class.DocReaction, {
// _id: upd.objectId,
// _class: upd.objectClass
// })
// ).shift()
// if (current === undefined) {
// No error, already removed
// }
// if (current.reactionType === ReactionKind.Star) {View on GitHub (pinned to 63e28dc964)
Solutions
- Check the reaction still exists (findAll with _id and _class) before sending the update tx
- Refresh client state after a removal so stale reaction ids are not updated
- Make the update idempotent: catch this error and treat it as a no-op if the reaction was already removed
- Verify _id and _class in the update payload match the original DocReaction
Example fix
// before
await tx.updateDoc(DocReaction.class, { _id: staleId }, { value: 5 })
// after
const current = await findAll(DocReaction.class, { _id: staleId })
if (current.length > 0) {
await tx.updateDoc(DocReaction.class, { _id: staleId }, { value: 5 })
} Defensive patterns
Strategy: try-catch
Validate before calling
const exists = (await findAll(ctx, rating.class.DocReaction, { _id: reactionId, _class: reactionClass })).length > 0
if (!exists) throw new SkipTx() Type guard
const isReactionUpdatable = async (ctx: any, id: string, cls: string) =>
(await findAll(ctx, rating.class.DocReaction, { _id: id, _class: cls })).length > 0 Try / catch
try {
await tx.updateDoc(reactionClass, { _id: id }, { value })
} catch (err) {
if ((err as Error).message === 'Reaction not found.') return // treat as idempotent no-op
throw err
} Prevention
- Re-fetch the reaction immediately before updating instead of using cached ids
- Treat reaction updates as idempotent and swallow not-found on replay
- Use cascading deletes so clients are notified when reactions are removed
- Log objectId/_class when this error occurs to spot stale-class bugs
When it happens
Trigger: Applying a TxUpdateDoc<DocReaction> whose objectId does not match any existing DocReaction with the matching _class — e.g. the reaction was deleted concurrently, the _id/class are wrong, or the update tx is replayed after the reaction was removed.
Common situations: Race condition where two clients remove/update the same reaction; replaying or re-applying a transaction log on a fresh/restore database; client caching a stale reaction id; passing an objectClass that does not match the stored class.
Related errors
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/c90af05a23333728.
Report an issue: GitHub.