RocketChat / RocketChat/Rocket.Chat

Livechat widget: `normalizeMessages()` async predicate in `Array.filter` is a no-op — thread-reply quoted context never renders

Open Beginner friendly
#42,002 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

type: bug
Dominant language
TypeScript
Stars
46.1k
Forks
13.9k
Avg merge
3d 3h
Merged PRs (30d)
130

Description

Description:

normalizeMessages() in packages/livechat/src/lib/threads.ts passes an async callback to Array.prototype.filter:

export const normalizeMessages = (messages: any[] = []): Promise<any[]> =>
	Promise.all(
		messages.filter(async (message) => {
			const result = await normalizeMessage(message);
			return result;
		}),
	);

Array.filter's predicate must be synchronous. An async function always returns a Promise, which is truthy, so filter keeps every message unchanged - normalizeMessage() never actually runs its intended logic. normalizeMessage() is meant to:

  1. drop thread-reply messages already registered as parents, and
  2. attach threadMsg / quoted-parent data to reply messages so Message/index.tsx can render a "replying to" preview bubble.

Because the filter is a no-op, thread replies shown in the Livechat widget render as flat standalone messages with no quoted-parent context.

This was introduced (and self-flagged) in #41559 during the TS migration - the PR left a FIXME comment noting it as a known latent bug surfaced by the type checker, to be "revisited separately," but it was never filed or fixed.

Steps to reproduce:
  1. Open the Livechat widget on a site with Livechat enabled.
  2. Start a conversation and reply in a thread from the agent side.
  3. Observe the reply as it renders in the visitor-facing widget message list.
Expected behavior:

Thread-reply messages should show a quoted preview of the parent message they're replying to (as Message/index.tsx is designed to render via threadMsg).

Actual behavior:

Thread replies render as plain, unannotated messages - no parent-message preview - because normalizeMessages() never filters/annotates the message array as intended.

Proposed fix:
export const normalizeMessages = async (messages: any[] = []): Promise<any[]> => {
	const normalized = await Promise.all(messages.map(normalizeMessage));
	return normalized.filter(Boolean);
};

This preserves normalizeMessage's existing contract (return null/falsy to drop a message) and needs no changes to callers (packages/livechat/src/lib/room.ts:256,290).

Server version:

N/A - client-side bug in the @rocket.chat/livechat widget package, reproducible on current develop.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/livechat/src/lib/threads.ts by reading normalizeMessage and normalizeMessages, then check their callers in packages/livechat/src/lib/room.ts at lines 256 and 290. Confirm the normalized result preserves intended filtering and threadMsg data, and verify that Message/index.tsx renders quoted parent context for thread replies.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
84/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.