Skip to content

fix(liveobjects): reject live object references as map values#2246

Open
sacOO7 wants to merge 1 commit into
mainfrom
fix/livemap-set-reject-live-object-references
Open

fix(liveobjects): reject live object references as map values#2246
sacOO7 wants to merge 1 commit into
mainfrom
fix/livemap-set-reject-live-object-references

Conversation

@sacOO7

@sacOO7 sacOO7 commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Problem

Passing an object obtained from the channel — an Instance or PathObject wrapper, or an internal LiveObject — as a value to a map write was not caught by validation. A very natural call like:

const a = map.get('a');     // returns an Instance
await map.set('b', a);      // 💥

passed LiveMap.validateKeyValue (which accepts any non-null object), fell through to the JSON encoding branch in primitiveToObjectData ({ json: value }), and then blew up at publish time inside wire encoding with:

TypeError: Converting circular structure to JSON

— a raw TypeError rather than a proper ErrorInfo, with no hint about what the user did wrong. (For a non-circular object it would instead silently serialize internal SDK state onto the wire.)

The TypeScript typings don't fully guard this either: the public LiveMap/LiveCounter branded types are inhabited by both the create() value types and the live objects in user type schemas, so this mistake can type-check.

Why rejecting (rather than supporting) is correct

Per the objects spec, object-valued writes accept only the creation value types returned by LiveMap.create() / LiveCounter.create(), which are evaluated into *_CREATE operations at write time (RTLM20a3, RTLM20e7g, validated per RTLMV4c). Assigning a reference to an existing object was deliberately removed from the write API when the value-type design replaced the old RTLM20e5a behaviour. A new spec point making the rejection explicit (RTLMV4c1: live objects and their PathObject/Instance wrappers must be rejected with 40013) is being added in the specification repo alongside this PR.

Fix

Add a check to LiveMap.validateKeyValue rejecting LiveObject, DefaultPathObject and DefaultInstance instances with ErrorInfo(40013, 400) and an actionable message pointing the user at LiveMap.create() / LiveCounter.create().

Because validateKeyValue is the single validation chokepoint, this covers all write paths: LiveMap.set, Instance.set, PathObject.set, BatchContext writes, and entry evaluation in LiveMap.create().

Testing

Extended the existing LiveMap.set throws on invalid input scenario with three cases: passing the map's own Instance, another Instance, and a PathObject as values. Verified the new assertions fail before the fix (with the Converting circular structure to JSON error above) and pass after it.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved validation to prevent assignment of existing live objects as map values. The system now properly rejects such operations with a helpful error message and guides users to create new values using the designated creation methods.

Passing an object obtained from the channel (an Instance or PathObject
wrapper, or an internal LiveObject) as a value to LiveMap.set /
Instance.set / PathObject.set, or as an entry value in LiveMap.create,
was not caught by validation: the value fell through to the JSON
encoding branch and failed at publish time with a confusing
"TypeError: Converting circular structure to JSON" instead of a proper
ErrorInfo.

Per the objects spec, only the LiveMap.create() / LiveCounter.create()
value types may be used to assign objects to map keys; references to
existing objects are not an accepted value type (RTLMV4c, new
RTLMV4c1). Reject them in LiveMap.validateKeyValue with an ErrorInfo
(code 40013, statusCode 400), which covers all write paths -
LiveMap.set, Instance.set, PathObject.set, BatchContext.set and
LiveMap.create entry evaluation - since they share this validation.

Co-Authored-By: Claude Fable 5 <[email protected]>
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR adds a validation guard to LiveMap.validateKeyValue that prevents existing live objects from being assigned as map values. The change imports DefaultInstance, extends the validation logic to reject object references, and adds test assertions verifying the new behavior.

Changes

LiveMap Value Reference Validation

Layer / File(s) Summary
LiveMap value validation guard
src/plugins/liveobjects/livemap.ts, test/realtime/liveobjects.test.js
Import DefaultInstance and extend validateKeyValue to reject assigning existing live objects (LiveObject, DefaultPathObject, DefaultInstance) as map values, throwing client.ErrorInfo with error code RTLMV4c1. Test verifies rejection of map, entryInstance, and entryPathObject references.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • VeskeR

Poem

🐰 A map must birth its own creations bright,
No borrowed beasts allowed in sight!
The guard now checks with careful eye,
Existing objects must not apply! ✨

🚥 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 accurately summarizes the main change: rejecting live object references as map values, which is the primary purpose of this fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/livemap-set-reject-live-object-references

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.43.0)
test/realtime/liveobjects.test.js

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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/realtime/liveobjects.test.js`:
- Around line 4090-4094: The new rejection tests for RTLMV4c1 only assert the
error message; update the calls to expectToThrowAsync used around map.set (the
three cases referencing map, entryInstance, entryPathObject) to also assert the
error object includes code: 40013 and statusCode: 400; specifically modify the
expectToThrowAsync invocations for these three assertions to verify both the
message ('Map value data type is unsupported') and that the thrown error has
code 40013 and statusCode 400 so the test enforces the PR's error-mapping
contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 298cdd7c-589c-4679-bdb7-d8b545e4951d

📥 Commits

Reviewing files that changed from the base of the PR and between 2a0eae5 and 2af076f.

📒 Files selected for processing (2)
  • src/plugins/liveobjects/livemap.ts
  • test/realtime/liveobjects.test.js

Comment on lines +4090 to +4094
// RTLMV4c1 - references to existing objects (Instance or PathObject) are not valid
// map values; only LiveMap.create()/LiveCounter.create() value types can assign objects
await expectToThrowAsync(async () => map.set('key', map), 'Map value data type is unsupported');
await expectToThrowAsync(async () => map.set('key', entryInstance), 'Map value data type is unsupported');
await expectToThrowAsync(async () => map.set('key', entryPathObject), 'Map value data type is unsupported');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert RTLMV4c1 error code/status in the new rejection checks.

These new cases currently verify only the message. Since this PR contract requires code: 40013 and statusCode: 400, add those assertions to prevent silent regressions in error mapping.

Suggested test tightening
-            await expectToThrowAsync(async () => map.set('key', map), 'Map value data type is unsupported');
-            await expectToThrowAsync(async () => map.set('key', entryInstance), 'Map value data type is unsupported');
-            await expectToThrowAsync(async () => map.set('key', entryPathObject), 'Map value data type is unsupported');
+            await expectToThrowAsync(async () => map.set('key', map), 'Map value data type is unsupported', {
+              withCode: 40013,
+              withStatusCode: 400,
+            });
+            await expectToThrowAsync(async () => map.set('key', entryInstance), 'Map value data type is unsupported', {
+              withCode: 40013,
+              withStatusCode: 400,
+            });
+            await expectToThrowAsync(async () => map.set('key', entryPathObject), 'Map value data type is unsupported', {
+              withCode: 40013,
+              withStatusCode: 400,
+            });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/realtime/liveobjects.test.js` around lines 4090 - 4094, The new
rejection tests for RTLMV4c1 only assert the error message; update the calls to
expectToThrowAsync used around map.set (the three cases referencing map,
entryInstance, entryPathObject) to also assert the error object includes code:
40013 and statusCode: 400; specifically modify the expectToThrowAsync
invocations for these three assertions to verify both the message ('Map value
data type is unsupported') and that the thrown error has code 40013 and
statusCode 400 so the test enforces the PR's error-mapping contract.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant