RocketChat/Rocket.Chat · error · Error

Invalid reaction

Error message

Invalid reaction

What it means

Thrown by the Apps Engine MessageBridge implementation when an App calls addReaction with a reaction string that fails the format check isValidReaction (must start and end with ':'). The bridge guards the input before delegating to executeSetReaction, which itself normalizes colons and validates the emoji against the built-in and custom emoji tables. It is a pure input-shape validation error, not a persistence or permissions error.

Source

Thrown at apps/meteor/app/apps/server/bridges/messages.ts:126

			case 'room':
				if (!username) {
					throw new Error('Invalid username');
				}

				notifications.notifyRoom(id, 'user-activity', username, isTyping ? ['user-typing'] : []);
				return;
			default:
				throw new Error('Unrecognized typing scope provided');
		}
	}

	private isValidReaction(reaction: Reaction): boolean {
		return reaction.startsWith(':') && reaction.endsWith(':');
	}

	protected async addReaction(messageId: string, userId: string, reaction: Reaction): Promise<void> {
		if (!this.isValidReaction(reaction)) {
			throw new Error('Invalid reaction');
		}

		return executeSetReaction(userId, reaction, messageId, true);
	}

	protected async removeReaction(messageId: string, userId: string, reaction: Reaction): Promise<void> {
		if (!this.isValidReaction(reaction)) {
			throw new Error('Invalid reaction');
		}

		return executeSetReaction(userId, reaction, messageId, false);
	}
}

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Wrap the reaction identifier in colons before calling addReaction: pass ':smile:' rather than 'smile'.
  2. Centralize reaction literals in a typed constant map keyed by colon-wrapped names so callers cannot pass a bare name.
  3. If the reaction comes from external input, run a normalize step: `reaction = reaction.startsWith(':') ? reaction : ':' + reaction; reaction = reaction.endsWith(':') ? reaction : reaction + ':';`
  4. Verify the emoji actually exists (built-in emoji.list or a custom emoji alias) before calling, otherwise executeSetReaction will throw a separate 'Invalid emoji provided' Meteor error even after this guard passes.

Example fix

// before
await modify.getUpdater().getReactionSetter().reactToMessage(messageId, 'thumbsup');

// after
await modify.getUpdater().getReactionSetter().reactToMessage(messageId, ':thumbsup:');
Defensive patterns

Strategy: validation

Validate before calling

function normalizeReaction(reaction: string): string {
  if (typeof reaction !== 'string' || reaction.length === 0) {
    throw new Error('Reaction must be a non-empty string');
  }
  let r = reaction;
  if (!r.startsWith(':')) r = ':' + r;
  if (!r.endsWith(':')) r = r + ':';
  return r;
}

// call before addReaction
const safe = normalizeReaction(reaction);

Type guard

function isValidReaction(reaction: unknown): reaction is string {
  return typeof reaction === 'string' && reaction.startsWith(':') && reaction.endsWith(':') && reaction.length >= 2;
}

Prevention

When it happens

Trigger: An App calls the messages accessor's reaction API (e.g. modify.getUpdater().getReactionSetter() or equivalent) passing a bare emoji name like 'smile' or '+1' instead of the colon-wrapped form ':smile:'. Also triggered if the reaction string is empty or only has one colon, since startsWith(':') && endsWith(':') would be false for ':' alone when length is 1 (startsWith and endsWith both true, so ':' passes — but ':smile' or 'smile:' fail). Any reaction literal lacking both bounding colons hits this branch.

Common situations: Apps authored against documentation examples that omit the colons; hardcoded emoji constants copied from picker output (which often drops colons); dynamically built reaction strings where the leading/trailing colon was stripped during templating; migration from an older Apps Engine version whose API tolerated bare names.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/d9e495f3d353dc06. Report an issue: GitHub.