Skip to content

fix: duplicate rooms in sidebar when a cached record _id is not a string - #42112

Open
sudoKrishna wants to merge 1 commit into
RocketChat:developfrom
sudoKrishna:fix/duplicate-rooms-non-string-id-42109
Open

fix: duplicate rooms in sidebar when a cached record _id is not a string#42112
sudoKrishna wants to merge 1 commit into
RocketChat:developfrom
sudoKrishna:fix/duplicate-rooms-non-string-id-42109

Conversation

@sudoKrishna

@sudoKrishna sudoKrishna commented Sep 12, 2026

Copy link
Copy Markdown

Fixes #42109

Problem

DocumentMapStore keys its cached records (rooms, subscriptions, etc.) by
record._id in a Map. _id is typed as string, but DDP/EJSON payloads
or manual DB writes can send a BSON ObjectId or EJSON binary wrapper
instead. Two "equal" but distinct instances of the same id are different
Map keys, so every re-merge of that record adds a new entry instead of
replacing it — the sidebar then shows duplicate rows for the same room.

Fix

Added normalizeId() in DocumentMapStore.ts to reduce any _id shape
(string, ObjectId via toHexString(), EJSON $binary) to a stable string,
applied everywhere _id is used as a Map key (store, storeMany,
replaceAll, has, get, delete, update, updateAsync, remove).

Testing

Added DocumentMapStore.spec.ts covering ObjectId/EJSON dupes, delete by
an equivalent instance, no over-collapsing of different ids, and a plain
string regression check. Before/after screenshots attached (failing on
develop, passing on this branch). eslint and tsc --noEmit clean.
before :
Screenshot From 2026-09-12 22-53-15
after :
Screenshot From 2026-09-12 22-53-56

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Fixed duplicate rooms and subscriptions appearing in the client cache or sidebar when records use non-string IDs.
    • Improved handling of BSON ObjectId and EJSON binary IDs so repeated records correctly replace existing entries.
    • Ensured records can be reliably retrieved, updated, and deleted using equivalent non-string IDs.

@sudoKrishna
sudoKrishna requested a review from a team as a code owner September 12, 2026 17:27
@changeset-bot

changeset-bot Bot commented Sep 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 0b3b46f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@rocket.chat/meteor Patch
@rocket.chat/core-typings Patch
@rocket.chat/rest-typings Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dionisio-bot

dionisio-bot Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: b157778a-8f43-49dd-a038-4a27ff88212f

📥 Commits

Reviewing files that changed from the base of the PR and between d6956ab and 0b3b46f.

📒 Files selected for processing (3)
  • .changeset/fix-duplicate-rooms-non-string-id.md
  • apps/meteor/client/lib/cachedStores/DocumentMapStore.spec.ts
  • apps/meteor/client/lib/cachedStores/DocumentMapStore.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (1)
apps/meteor/client/lib/cachedStores/DocumentMapStore.ts (1)

22-22: 🗄️ Data Integrity & Integration

IDocumentMapStore and all cached-store record types require _id: string. normalizeId intentionally canonicalizes non-string runtime representations, and store, has, get, and delete use that same canonical key. The existing tests also require equivalent ObjectId-like instances to replace one record. Therefore, an equivalent string and ObjectId-like value are not established as distinct identifiers, and adding namespaces would break cross-representation lookups and deletion.


Walkthrough

DocumentMapStore now normalizes non-string _id values before map operations. Tests cover ObjectId-like, EJSON binary, distinct, deletion, and string identifiers. A patch changeset documents the duplicate room and subscription fix.

Changes

DocumentMapStore identifier normalization

Layer / File(s) Summary
Normalize identifiers across store operations
apps/meteor/client/lib/cachedStores/DocumentMapStore.ts
The store converts supported non-string _id values to stable strings. All lookup, storage, update, deletion, and removal operations use normalized keys.
Validate normalization and record the patch
apps/meteor/client/lib/cachedStores/DocumentMapStore.spec.ts, .changeset/fix-duplicate-rooms-non-string-id.md
Tests verify replacement and deletion for equivalent identifiers, separation of distinct identifiers, and unchanged string-ID behavior. The changeset records a patch release.اً

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested labels: type: bug

Suggested reviewers: tassoevan

Merge Risk: ⚪ Minimal · up to 0b3b4

Identifier normalization is applied across the store’s key operations and covered by focused tests for equivalent, distinct, deletion, and string identifiers.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix: preventing duplicate sidebar rooms when cached record _id values are non-string.
Linked Issues check ✅ Passed Issue #42109 requires stable keys for equivalent non-string _id values, replacement of duplicate records, and preservation of distinct and string IDs. DocumentMapStore.ts adds normalizeId() for …
Out of Scope Changes check ✅ Passed The changeset documents the fix. The new tests verify the linked issue behavior. The implementation changes only DocumentMapStore ID key handling and related store operations. No unrelated changes a…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…

Warning

Errors were encountered while retrieving linked issues.

Errors (1)
  • JIRA integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/meteor/client/lib/cachedStores/DocumentMapStore.ts">

<violation number="1" location="apps/meteor/client/lib/cachedStores/DocumentMapStore.ts:22">
P2: When a valid string `_id` shares its text with an ObjectId (or a binary ID shares bytes with another subtype), `normalizeId` maps both records to the same key and the store drops one. Preserve the BSON representation/type (and binary subtype) in the normalized key instead of returning an untagged payload string.</violation>
</file>

<file name="apps/meteor/client/lib/cachedStores/DocumentMapStore.spec.ts">

<violation number="1" location="apps/meteor/client/lib/cachedStores/DocumentMapStore.spec.ts:36">
P3: The PR description states normalizeId() was applied to store/storeMany/replaceAll/has/get/delete/update/updateAsync/remove, but the spec only exercises store, delete, and get. has, storeMany, replaceAll, update, updateAsync, and remove all take a non-string _id through normalizeId and are the easiest places to regress the exact bug being fixed; add cases that store an ObjectId/EJSON _id and assert these methods hit the same normalized key (e.g. has() after an ObjectId store, and delete() of a binary _id stored via storeMany).</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

}

if (typeof (id as { toHexString?: unknown }).toHexString === 'function') {
return (id as unknown as { toHexString: () => string }).toHexString();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When a valid string _id shares its text with an ObjectId (or a binary ID shares bytes with another subtype), normalizeId maps both records to the same key and the store drops one. Preserve the BSON representation/type (and binary subtype) in the normalized key instead of returning an untagged payload string.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/client/lib/cachedStores/DocumentMapStore.ts, line 22:

<comment>When a valid string `_id` shares its text with an ObjectId (or a binary ID shares bytes with another subtype), `normalizeId` maps both records to the same key and the store drops one. Preserve the BSON representation/type (and binary subtype) in the normalized key instead of returning an untagged payload string.</comment>

<file context>
@@ -1,5 +1,38 @@
+	}
+
+	if (typeof (id as { toHexString?: unknown }).toHexString === 'function') {
+		return (id as unknown as { toHexString: () => string }).toHexString();
+	}
+
</file context>

expect(useStore.getState().records.size).toBe(1);
});

it('deletes a record stored with a non-string _id when given an equivalent but distinct instance', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The PR description states normalizeId() was applied to store/storeMany/replaceAll/has/get/delete/update/updateAsync/remove, but the spec only exercises store, delete, and get. has, storeMany, replaceAll, update, updateAsync, and remove all take a non-string _id through normalizeId and are the easiest places to regress the exact bug being fixed; add cases that store an ObjectId/EJSON _id and assert these methods hit the same normalized key (e.g. has() after an ObjectId store, and delete() of a binary _id stored via storeMany).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/client/lib/cachedStores/DocumentMapStore.spec.ts, line 36:

<comment>The PR description states normalizeId() was applied to store/storeMany/replaceAll/has/get/delete/update/updateAsync/remove, but the spec only exercises store, delete, and get. has, storeMany, replaceAll, update, updateAsync, and remove all take a non-string _id through normalizeId and are the easiest places to regress the exact bug being fixed; add cases that store an ObjectId/EJSON _id and assert these methods hit the same normalized key (e.g. has() after an ObjectId store, and delete() of a binary _id stored via storeMany).</comment>

<file context>
@@ -0,0 +1,64 @@
+			expect(useStore.getState().records.size).toBe(1);
+		});
+
+		it('deletes a record stored with a non-string _id when given an equivalent but distinct instance', () => {
+			const useStore = createDocumentMapStore<ITestRecord>();
+
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Duplicate rooms in the sidebar when a cached record _id is not a string

1 participant