Skip to content

Fix LogBox parsing for cross-runtime jsi::JSErrors - #55994

Open
Naim08 wants to merge 1 commit into
react:mainfrom
Naim08:fix/jsi-error-forwarding
Open

Fix LogBox parsing for cross-runtime jsi::JSErrors#55994
Naim08 wants to merge 1 commit into
react:mainfrom
Naim08:fix/jsi-error-forwarding

Conversation

@Naim08

@Naim08 Naim08 commented Mar 8, 2026

Copy link
Copy Markdown

Summary

When a jsi::JSError is thrown from a background Hermes runtime and caught in the RuntimeScheduler, the error's jsi::Value belongs to a different jsi::Runtime. Forwarding this cross-runtime value to onTaskError_ is undefined behavior and causes LogBox to display "Unknown" instead of the actual error message.

This PR introduces a reportError() helper in both RuntimeScheduler_Legacy and RuntimeScheduler_Modern that:

  • Re-creates the error as a proper Error instance in the current runtime using the safe C++ string accessors (getMessage() / getStack())
  • Preserves the original message and stack trace
  • Falls back to a simple jsi::JSError if reconstruction fails

Changelog:

[GENERAL] [FIXED] - Fix LogBox showing "Unknown" for errors originating from background Hermes runtimes

Test plan:

  • Added handlingCrossRuntimeError test: throws a jsi::JSError from a second Hermes runtime and verifies the error message is correctly forwarded
  • Added handlingErrorPreservesMessage test: verifies same-runtime errors still work correctly with the new code path
  • Updated StubErrorUtils to capture and expose the last reported error message for assertion

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings March 8, 2026 17:31
@meta-cla

meta-cla Bot commented Mar 8, 2026

Copy link
Copy Markdown

Hi @Naim08!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

Copilot AI 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.

Pull request overview

Fixes LogBox error display for tasks that throw jsi::JSError originating from a different Hermes runtime by avoiding cross-runtime jsi::Value usage and reconstructing an Error object in the current runtime before reporting.

Changes:

  • Add reportError() helper to Legacy/Modern RuntimeScheduler implementations to reconstruct and forward errors safely in the current runtime.
  • Update scheduler error handling call sites to use reportError() for jsi::JSError.
  • Extend test scaffolding and add new tests to validate cross-runtime error forwarding and message preservation.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/StubErrorUtils.h Capture last reported error message from ErrorUtils.reportFatalError for assertions.
packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp Add tests covering cross-runtime jsi::JSError handling and message preservation.
packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h Declare reportError() helper and document cross-runtime error rationale.
packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp Implement reportError() and route jsi::JSError handling through it.
packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.{h,cpp} Add/implement reportError() and route jsi::JSError handling through it.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines 76 to +77
int reportFatalCallCount_;
std::string lastReportedMessage_;

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

StubErrorUtils leaves reportFatalCallCount_ uninitialized. Because the object is created with the implicit default constructor, the first reportFatalCallCount_++ is undefined behavior and can make the tests flaky. Initialize this member (and lastReportedMessage_) with in-class initializers or an explicit constructor.

Suggested change
int reportFatalCallCount_;
std::string lastReportedMessage_;
int reportFatalCallCount_{0};
std::string lastReportedMessage_{};

Copilot uses AI. Check for mistakes.
Comment on lines 48 to +55
[this](
jsi::Runtime &runtime, const jsi::Value &, const jsi::Value *arguments, size_t) noexcept -> jsi::Value {
jsi::Runtime &runtime, const jsi::Value &, const jsi::Value *arguments, size_t count) noexcept -> jsi::Value {
reportFatalCallCount_++;
if (count > 0 && arguments[0].isObject()) {
auto obj = arguments[0].getObject(runtime);
auto msgVal = obj.getProperty(runtime, "message");
if (msgVal.isString()) {
lastReportedMessage_ = msgVal.getString(runtime).utf8(runtime);

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

The host function lambda for reportFatalError is declared noexcept, but the newly added JSI operations (getObject, getProperty, getString) can throw jsi::JSIException. If any of those throw, the process will std::terminate(). Remove noexcept here or wrap the property access in a try/catch to keep the stub safe.

Copilot uses AI. Check for mistakes.
Comment on lines 50 to +57
reportFatalCallCount_++;
if (count > 0 && arguments[0].isObject()) {
auto obj = arguments[0].getObject(runtime);
auto msgVal = obj.getProperty(runtime, "message");
if (msgVal.isString()) {
lastReportedMessage_ = msgVal.getString(runtime).utf8(runtime);
}
}

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

lastReportedMessage_ is only updated when a valid message string is found; otherwise it retains its previous value. Consider clearing it at the start of each reportFatalError call (or explicitly setting it to an empty string when parsing fails) to avoid stale assertions if multiple errors are reported.

Copilot uses AI. Check for mistakes.
error.getStack());
onTaskError_(runtime, localError);
} catch (...) {
jsi::JSError fallbackError(runtime, error.getMessage());

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

In the fallback path you construct jsi::JSError with only error.getMessage(), which drops the original stack trace even though error.getStack() is available as a safe C++ string. Consider using the JSError(Runtime&, message, stack) constructor here so LogBox still gets a stack when reconstruction fails.

Suggested change
jsi::JSError fallbackError(runtime, error.getMessage());
jsi::JSError fallbackError(
runtime,
error.getMessage(),
error.getStack());

Copilot uses AI. Check for mistakes.
error.getStack());
onTaskError_(runtime, localError);
} catch (...) {
jsi::JSError fallbackError(runtime, error.getMessage());

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

The fallback path reconstructs a jsi::JSError with only the message, which discards error.getStack() even though it is already available and safe. Consider using the JSError(Runtime&, message, stack) constructor in the fallback to preserve the original stack trace when Error reconstruction fails.

Suggested change
jsi::JSError fallbackError(runtime, error.getMessage());
jsi::JSError fallbackError(
runtime,
error.getMessage(),
error.getStack());

Copilot uses AI. Check for mistakes.
@meta-cla

meta-cla Bot commented Mar 8, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Mar 8, 2026
@facebook-github-bot facebook-github-bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Mar 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants