Skip to content

Conversation

@lodyai
Copy link
Contributor

@lodyai lodyai bot commented Dec 31, 2025

Problem

When setting state with objects containing undefined values (e.g., { name: 'test', age: undefined }), the code would throw a "State diverged" error. This happened because undefined was being converted to null when written to LoroDoc, but the in-memory state retained the undefined value, causing a mismatch during consistency checks.

Solution

Treat undefined values as non-existent fields - they should be ignored rather than being stored.

Changes

packages/core/src/core/diff.ts (diffMap function)

  • Skip keys with undefined values when iterating newStateObj
  • Generate delete changes if the old state had a non-undefined value for that key

packages/core/src/core/mirror.ts (initializeContainer method)

  • Skip undefined values when initializing Map containers

packages/core/src/core/utils.ts (new stripUndefined function)

  • Recursively removes undefined values from objects
  • Preserves non-enumerable properties (like $cid)
  • Optimization: returns original object if no undefined values are found (preserves reference equality)

packages/core/src/core/mirror.ts (setState method)

  • Apply stripUndefined to newState before storing

New test file: packages/core/tests/undefined-in-map.test.ts

  • Tests handling of undefined fields in Maps
  • Tests handling of undefined fields in nested Maps
  • Tests handling of undefined in schema.Any() fields

Testing

All 212 tests pass.

When setting state with objects containing undefined values like
{ name: 'test', age: undefined }, the undefined fields should be
treated as if they don't exist, rather than being converted to null.

Changes:
- diffMap: Skip undefined values when iterating newStateObj, and
  generate delete changes if the old value existed
- initializeContainer: Skip undefined values when initializing Map
- stripUndefined: New utility function to recursively remove undefined
  values from objects while preserving non-enumerable properties ()
- setState: Apply stripUndefined to newState before storing

This prevents state divergence errors when users pass objects with
undefined values to setState.

Co-authored-by: lody <agent@lody.ai>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

- Use Object.create(null) instead of {} to create result objects
- Skip unsafe keys (__proto__, constructor, prototype)
- Use Object.defineProperty instead of direct assignment for copying values
- Use Map instead of plain object for strippedValues to avoid pollution

This prevents attackers from polluting Object.prototype via JSON input
containing __proto__ or similar keys.

Co-authored-by: lody <agent@lody.ai>
Co-authored-by: lody <agent@lody.ai>
- Assert that undefined fields do not exist in state using 'key in obj'
- Assert that accessing undefined fields returns undefined
- Add test case for deleting existing field by setting to undefined

Co-authored-by: lody <agent@lody.ai>
@zxch3n zxch3n merged commit 3aa6c97 into main Jan 2, 2026
1 check passed
@github-actions github-actions bot mentioned this pull request Jan 2, 2026
@zxch3n zxch3n deleted the fix/treat-undefined-as-nonexistent-field branch January 2, 2026 11:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants