Skip to content

fix(react/runtime): optimize main thread event error message#1838

Merged
Yradex merged 1 commit intolynx-family:mainfrom
Yradex:fix/mts-error-msg
Sep 28, 2025
Merged

fix(react/runtime): optimize main thread event error message#1838
Yradex merged 1 commit intolynx-family:mainfrom
Yradex:fix/mts-error-msg

Conversation

@Yradex
Copy link
Copy Markdown
Collaborator

@Yradex Yradex commented Sep 28, 2025

Add a new error msg:

main-thread.js exception: "main-thread:bindtap" on <view> (snapshot -5 "__Card__:__snapshot_835da_22b86_1") expected a main-thread function but received function (anonymous). Did you forget to add a "main thread" directive to the handler?

@coderabbitai summary

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).
  • Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).

@Yradex Yradex requested a review from hzy as a code owner September 28, 2025 07:08
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Sep 28, 2025

🦋 Changeset detected

Latest commit: 648719e

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

This PR includes changesets to release 1 package
Name Type
@lynx-js/react 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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Sep 28, 2025

📝 Walkthrough

Walkthrough

Introduces describeInvalidValue utility and integrates validation into worklet event handling. Updates updateWorkletEvent signature and DEV-time error reporting. Adds tests for invalid value descriptions and worklet event scenarios. Adjusts snapshots accordingly. Includes a changeset updating @lynx-js/react with a patch note.

Changes

Cohort / File(s) Summary
Changeset
./.changeset/major-rivers-happen.md
Adds patch changeset for @lynx-js/react, noting “optimize main thread event error message.”
Debug utility
packages/react/runtime/src/debug/describeInvalidValue.ts
Adds exported describeInvalidValue(value: unknown): string to format human-readable descriptions for invalid values.
Worklet event handling
packages/react/runtime/src/snapshot/workletEvent.ts
Imports describeInvalidValue; adds formatting/report helpers; updates updateWorkletEvent(snapshot, expIndex, oldValue, elementIndex, workletType, eventType, eventName) to validate non-object values in DEV and report detailed errors; uses rawValue with fallback to {}.
Debug tests
packages/react/runtime/__test__/debug/describeInvalidValue.test.ts
Adds tests covering null/undefined, functions (named/anonymous), primitives, symbols, and default object case for describeInvalidValue.
Snapshot/worklet tests
packages/react/runtime/__test__/snapshot/workletEvent.test.jsx
Imports updateWorkletEvent; adds test for background function bound to main-thread event; extends DEV warnings for invalid handlers and names; updates snapshots (e.g., __snapshot_a94a8_test_15).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

framework:React

Suggested reviewers

  • hzy
  • colinaaa

Poem

I thump my paw at threads that stray,
With whiskers twitch—errors, away!
New words to name the odd and wild,
Worklets behave, both tame and styled.
Snapshots hop to the latest scene,
Bunny approves: events kept clean. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title accurately reflects the key change, specifying a fix in the react/runtime module aimed at optimizing the main thread event error message. It concisely summarizes the primary objective of the PR without unnecessary detail or noise. It follows a conventional commit style, making it easy to scan in history. It is focused and relevant to the core modifications in the changeset.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@codecov
Copy link
Copy Markdown

codecov bot commented Sep 28, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (6)
packages/react/runtime/src/debug/describeInvalidValue.ts (1)

5-25: Make descriptions more informative (arrays) and guard very long strings.

Current fallback for objects is fine, but two small wins:

  • Identify arrays explicitly to reduce confusion.
  • Truncate very long strings in messages to avoid noisy logs.

Suggested change:

 export function describeInvalidValue(value: unknown): string {
+  const MAX_STRING_PREVIEW = 120;
   if (value === null) {
     return 'null';
   }
   if (value === undefined) {
     return 'undefined';
   }
   if (typeof value === 'function') {
     return `function ${value.name || '(anonymous)'}`;
   }
   if (typeof value === 'string') {
-    return `string "${value}"`;
+    const s = value.length <= MAX_STRING_PREVIEW
+      ? value
+      : value.slice(0, MAX_STRING_PREVIEW) + '…';
+    return `string "${s}"`;
   }
   if (typeof value === 'number' || typeof value === 'bigint' || typeof value === 'boolean') {
     return `${typeof value} ${String(value)}`;
   }
   if (typeof value === 'symbol') {
     return `symbol ${String(value)}`;
   }
+  if (Array.isArray(value)) {
+    return `array(length ${value.length})`;
+  }
   return `unexpected ${typeof value}`;
 }
packages/react/runtime/__test__/debug/describeInvalidValue.test.ts (1)

29-32: Add coverage for arrays and very long strings.

Consider asserting:

  • arrays: expect(...).toBe('array(length 3)');
  • long strings are truncated with an ellipsis.

Keeps tests aligned with more descriptive messaging if adopted.

packages/react/runtime/__test__/snapshot/workletEvent.test.jsx (2)

424-508: Strengthen expectation to validate value description is surfaced.

In addition to checking the attribute name, assert the error message includes the described invalid value (e.g., contains 'function'). This guards the improved error formatting.

Example:

-        expect(reportErrorSpy).toHaveBeenCalledWith(
-          expect.objectContaining({
-            message: expect.stringContaining('"main-thread:bindtap"'),
-          }),
-        );
+        expect(reportErrorSpy).toHaveBeenCalledWith(
+          expect.objectContaining({
+            message: expect.stringContaining('"main-thread:bindtap"'),
+          }),
+        );
+        expect(reportErrorSpy.mock.calls[0][0].message).toEqual(
+          expect.stringContaining('function')
+        );

510-559: Dev warnings: assert more of the formatted context.

Also assert the message includes:

  • element tag ()
  • snapshot id and name
  • the described value, e.g., string "not-a-worklet"

Example:

-      expect(getErrorMessage(reportErrorSpy)).toContain('"main-thread:bindtap" on <view>');
+      const msg = getErrorMessage(reportErrorSpy);
+      expect(msg).toContain('"main-thread:bindtap" on <view>');
+      expect(msg).toContain('snapshot 42 "TestSnapshot"');
+      expect(msg).toContain('string "not-a-worklet"');
packages/react/runtime/src/snapshot/workletEvent.ts (2)

16-34: Tune wording for accuracy and consistency.

Message says “expected a main-thread function,” while the runtime expects a worklet-like object. Consider “main-thread worklet/handler” to reduce ambiguity.

-  const message = `"${eventAttr}" on <${elementTag}> (snapshot ${elementId} "${snapshotName}") expected `
-    + 'a main-thread function but received '
+  const message = `"${eventAttr}" on <${elementTag}> (snapshot ${elementId} "${snapshotName}") expected `
+    + 'a main-thread worklet/handler but received '
     + `${describeInvalidValue(value)}. Did you forget to add a "main thread" directive to the handler?`;

47-53: DEV validation: also reject arrays and obviously non‑worklet objects.

Today only non-objects are rejected. Arrays or plain objects without worklet markers slip through and may fail later. Recommend expanding the DEV check:

-  const rawValue = snapshot.__values![expIndex];
-  if (__DEV__ && rawValue !== null && rawValue !== undefined && typeof rawValue !== 'object') {
-    reportInvalidWorkletValue(snapshot, elementIndex, workletType, eventType, eventName, rawValue);
-    return;
-  }
+  const rawValue = snapshot.__values![expIndex];
+  if (__DEV__) {
+    const isObject = rawValue !== null && rawValue !== undefined && typeof rawValue === 'object';
+    const isArray = Array.isArray(rawValue);
+    const isWorkletLike =
+      isObject && !isArray && (('_wkltId' in (rawValue as any)) || ('_lepusWorkletHash' in (rawValue as any)));
+    if (!isObject || isArray || !isWorkletLike) {
+      reportInvalidWorkletValue(snapshot, elementIndex, workletType, eventType, eventName, rawValue);
+      return;
+    }
+  }

This keeps prod behavior unchanged but gives earlier, clearer feedback in DEV.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 316da85 and 648719e.

📒 Files selected for processing (5)
  • .changeset/major-rivers-happen.md (1 hunks)
  • packages/react/runtime/__test__/debug/describeInvalidValue.test.ts (1 hunks)
  • packages/react/runtime/__test__/snapshot/workletEvent.test.jsx (6 hunks)
  • packages/react/runtime/src/debug/describeInvalidValue.ts (1 hunks)
  • packages/react/runtime/src/snapshot/workletEvent.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/*.md

📄 CodeRabbit inference engine (AGENTS.md)

For contributions, always generate a changeset and commit the resulting markdown file(s)

Files:

  • .changeset/major-rivers-happen.md
🧠 Learnings (2)
📚 Learning: 2025-09-12T09:43:04.847Z
Learnt from: gaoachao
PR: lynx-family/lynx-stack#1736
File: .changeset/spotty-experts-smoke.md:1-3
Timestamp: 2025-09-12T09:43:04.847Z
Learning: In the lynx-family/lynx-stack repository, private packages (marked with "private": true in package.json) like lynx-js/react-transform don't require meaningful changeset entries even when their public APIs change, since they are not published externally and only affect internal development.

Applied to files:

  • .changeset/major-rivers-happen.md
📚 Learning: 2025-09-12T09:43:04.847Z
Learnt from: gaoachao
PR: lynx-family/lynx-stack#1736
File: .changeset/spotty-experts-smoke.md:1-3
Timestamp: 2025-09-12T09:43:04.847Z
Learning: In the lynx-family/lynx-stack repository, empty changeset files (containing only `---\n\n---`) are used for internal changes that modify src/** files but don't require meaningful release notes, such as private package changes or testing-only modifications. This satisfies CI requirements without generating user-facing release notes.

Applied to files:

  • .changeset/major-rivers-happen.md
🧬 Code graph analysis (3)
packages/react/runtime/__test__/snapshot/workletEvent.test.jsx (3)
packages/react/runtime/__test__/utils/envManager.ts (1)
  • globalEnvManager (86-86)
packages/react/testing-library/src/pure.jsx (2)
  • render (39-108)
  • waitSchedule (14-20)
packages/react/runtime/src/snapshot/workletEvent.ts (1)
  • updateWorkletEvent (65-65)
packages/react/runtime/__test__/debug/describeInvalidValue.test.ts (1)
packages/react/runtime/src/debug/describeInvalidValue.ts (1)
  • describeInvalidValue (5-25)
packages/react/runtime/src/snapshot/workletEvent.ts (3)
packages/react/runtime/src/snapshot.ts (1)
  • SnapshotInstance (278-658)
packages/react/runtime/src/debug/describeInvalidValue.ts (1)
  • describeInvalidValue (5-25)
packages/react/worklet-runtime/src/bindings/types.ts (1)
  • Worklet (41-52)
🔇 Additional comments (3)
.changeset/major-rivers-happen.md (1)

1-6: Changeset present and scoped correctly.

Patch entry for "@lynx-js/react" with a concise message looks good and satisfies CI/release requirements. Based on learnings.

packages/react/runtime/src/snapshot/workletEvent.ts (2)

11-14: Attribute formatter reads well and matches expected shape.

Handles both 'bindEvent' and 'bind' inputs cleanly.


35-43: No changes needed; all updateWorkletEvent callers are passing seven arguments in the correct order.

@relativeci
Copy link
Copy Markdown

relativeci bot commented Sep 28, 2025

Web Explorer

#5568 Bundle Size — 365.44KiB (0%).

648719e(current) vs 316da85 main#5554(baseline)

Bundle metrics  Change 1 change
                 Current
#5568
     Baseline
#5554
No change  Initial JS 145.71KiB 145.71KiB
No change  Initial CSS 32KiB 32KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 8 8
No change  Assets 8 8
Change  Modules 217(-0.91%) 219
No change  Duplicate Modules 16 16
No change  Duplicate Code 3.37% 3.37%
No change  Packages 4 4
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#5568
     Baseline
#5554
No change  JS 239.42KiB 239.42KiB
No change  Other 94.02KiB 94.02KiB
No change  CSS 32KiB 32KiB

Bundle analysis reportBranch Yradex:fix/mts-error-msgProject dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci bot commented Sep 28, 2025

React Example

#5571 Bundle Size — 237.56KiB (0%).

648719e(current) vs 316da85 main#5557(baseline)

Bundle metrics  Change 1 change
                 Current
#5571
     Baseline
#5557
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 166 166
No change  Duplicate Modules 68 68
Change  Duplicate Code 46.82%(+0.02%) 46.81%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#5571
     Baseline
#5557
No change  IMG 145.76KiB 145.76KiB
No change  Other 91.8KiB 91.8KiB

Bundle analysis reportBranch Yradex:fix/mts-error-msgProject dashboard


Generated by RelativeCIDocumentationReport issue

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Sep 28, 2025

CodSpeed Performance Report

Merging #1838 will improve performances by 5.58%

Comparing Yradex:fix/mts-error-msg (648719e) with main (316da85)

🎉 Hooray! codspeed-node just leveled up to 4.0.1!

A heads-up, this is a breaking change and it might affect your current performance baseline a bit. But here's the exciting part - it's packed with new, cool features and promises improved result stability 🥳!
Curious about what's new? Visit our releases page to delve into all the awesome details about this new version.

Summary

⚡ 1 improvement
✅ 52 untouched
⏩ 3 skipped1

Benchmarks breakdown

Benchmark BASE HEAD Change
transform 1000 view elements 43.6 ms 41.3 ms +5.58%

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@Yradex Yradex enabled auto-merge (squash) September 28, 2025 07:45
@Yradex Yradex merged commit 7a554e2 into lynx-family:main Sep 28, 2025
84 of 86 checks passed
@Yradex Yradex deleted the fix/mts-error-msg branch September 28, 2025 07:47
colinaaa pushed a commit that referenced this pull request Oct 1, 2025
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @lynx-js/react@0.114.1

### Patch Changes

- Add `event.stopPropagation` and `event.stopImmediatePropagation` in
MTS, to help with event propagation control
([#1835](#1835))

    ```tsx
    function App() {
      function handleInnerTap(event: MainThread.TouchEvent) {
        "main thread";
        event.stopPropagation();
        // Or stop immediate propagation with
        // event.stopImmediatePropagation();
      }

      // OuterTap will not be triggered
      return (
        <view main-thread:bindtap={handleOuterTap}>
          <view main-thread:bindtap={handleInnerTap}>
            <text>Hello, world</text>
          </view>
        </view>
      );
    }
    ```

Note, if this feature is used in [Lazy Loading Standalone
Project](https://lynxjs.org/react/code-splitting.html#lazy-loading-standalone-project),
both the Producer and the Consumer should update to latest version of
`@lynx-js/react` to make sure the feature is available.

- Fix the "ReferenceError: Node is not defined" error.
([#1850](#1850))

This error would happen when upgrading to `@testing-library/jest-dom`
[v6.9.0](https://github.com/testing-library/jest-dom/releases/tag/v6.9.0).

- fix: optimize main thread event error message
([#1838](#1838))

## @lynx-js/rspeedy@0.11.5

### Patch Changes

- Bump Rsbuild v1.5.13 with Rspack v1.5.8.
([#1849](#1849))

## @lynx-js/react-rsbuild-plugin@0.11.1

### Patch Changes

- Updated dependencies
\[[`19f823a`](19f823a)]:
    -   @lynx-js/css-extract-webpack-plugin@0.6.4
    -   @lynx-js/react-alias-rsbuild-plugin@0.11.1
    -   @lynx-js/use-sync-external-store@1.5.0
    -   @lynx-js/react-refresh-webpack-plugin@0.3.4
    -   @lynx-js/react-webpack-plugin@0.7.1

## @lynx-js/testing-environment@0.1.8

### Patch Changes

- Fix the "ReferenceError: Node is not defined" error.
([#1850](#1850))

This error would happen when upgrading to `@testing-library/jest-dom`
[v6.9.0](https://github.com/testing-library/jest-dom/releases/tag/v6.9.0).

## @lynx-js/web-constants@0.17.2

### Patch Changes

- feat: support load bts chunk from remote address
([#1834](#1834))

    -   re-support chunk splitting
    -   support lynx.requireModule with a json file
- support lynx.requireModule, lynx.requireModuleAsync with a remote url
- support to add a breakpoint in chrome after reloading the web page

-   Updated dependencies \[]:
    -   @lynx-js/web-worker-rpc@0.17.2

## @lynx-js/web-core@0.17.2

### Patch Changes

- feat: support load bts chunk from remote address
([#1834](#1834))

    -   re-support chunk splitting
    -   support lynx.requireModule with a json file
- support lynx.requireModule, lynx.requireModuleAsync with a remote url
- support to add a breakpoint in chrome after reloading the web page

- Updated dependencies
\[[`a35a245`](a35a245)]:
    -   @lynx-js/web-worker-runtime@0.17.2
    -   @lynx-js/web-constants@0.17.2
    -   @lynx-js/web-mainthread-apis@0.17.2
    -   @lynx-js/web-worker-rpc@0.17.2

## @lynx-js/web-mainthread-apis@0.17.2

### Patch Changes

- Updated dependencies
\[[`a35a245`](a35a245)]:
    -   @lynx-js/web-constants@0.17.2
    -   @lynx-js/web-style-transformer@0.17.2

## @lynx-js/web-worker-runtime@0.17.2

### Patch Changes

- feat: support load bts chunk from remote address
([#1834](#1834))

    -   re-support chunk splitting
    -   support lynx.requireModule with a json file
- support lynx.requireModule, lynx.requireModuleAsync with a remote url
- support to add a breakpoint in chrome after reloading the web page

- Updated dependencies
\[[`a35a245`](a35a245)]:
    -   @lynx-js/web-constants@0.17.2
    -   @lynx-js/web-mainthread-apis@0.17.2
    -   @lynx-js/web-worker-rpc@0.17.2

## @lynx-js/css-extract-webpack-plugin@0.6.4

### Patch Changes

- Avoid generating `.css.hot-update.json` when HMR is disabled.
([#1811](#1811))

## create-rspeedy@0.11.5



## @lynx-js/react-alias-rsbuild-plugin@0.11.1



## upgrade-rspeedy@0.11.5



## @lynx-js/web-core-server@0.17.2



## @lynx-js/web-rsbuild-server-middleware@0.17.2



## @lynx-js/web-style-transformer@0.17.2



## @lynx-js/web-worker-rpc@0.17.2

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

2 participants