@@ -380,7 +380,42 @@ void RuntimeScheduler_Modern::executeTask(
380380 task.callback = result.getObject (runtime).getFunction (runtime);
381381 }
382382 } catch (jsi::JSError& error) {
383- onTaskError_ (runtime, error);
383+ // The JSError may originate from a different Hermes runtime (e.g. an extra
384+ // runtime created by a NativeModule). Forwarding error.value() directly to
385+ // ErrorUtils.reportFatalError would pass a jsi::Value that belongs to the
386+ // source runtime's heap into the main runtime, which is undefined behaviour
387+ // and causes LogBox to display "Unknown" instead of the real message.
388+ //
389+ // Fix: reconstruct a proper `new Error(message)` in the correct (main)
390+ // runtime and copy the already-extracted stack string onto it, then wrap it
391+ // with the JSError(Value&&, string, string) constructor that does NOT
392+ // re-read any properties from the runtime - so only safe std::strings cross
393+ // the runtime boundary.
394+ try {
395+ auto errorMessage = error.getMessage ();
396+ auto errorStack = error.getStack ();
397+ // Build `new Error(message)` in the main runtime.
398+ jsi::Value newErrorObj =
399+ runtime.global ()
400+ .getPropertyAsFunction (runtime, " Error" )
401+ .callAsConstructor (
402+ runtime,
403+ jsi::String::createFromUtf8 (runtime, errorMessage));
404+ // Override .stack with the original cross-runtime stack string.
405+ newErrorObj.getObject (runtime).setProperty (
406+ runtime,
407+ " stack" ,
408+ jsi::String::createFromUtf8 (runtime, errorStack));
409+ // Use the no-runtime-read constructor to avoid any cross-runtime access.
410+ jsi::JSError newError (
411+ std::move (newErrorObj), errorMessage, errorStack);
412+ onTaskError_ (runtime, newError);
413+ } catch (...) {
414+ // Reconstruction failed (e.g. Error constructor not available yet).
415+ // Fall back to a plain string error so we at least surface the message.
416+ jsi::JSError fallback (runtime, error.getMessage ());
417+ onTaskError_ (runtime, fallback);
418+ }
384419 } catch (std::exception& ex) {
385420 jsi::JSError error (runtime, std::string (" Non-js exception: " ) + ex.what ());
386421 onTaskError_ (runtime, error);
@@ -419,7 +454,28 @@ void RuntimeScheduler_Modern::performMicrotaskCheckpoint(
419454 break ;
420455 }
421456 } catch (jsi::JSError& error) {
422- onTaskError_ (runtime, error);
457+ // Same cross-runtime fix as in executeTask: reconstruct a proper
458+ // new Error in the main runtime before forwarding to ErrorUtils.
459+ try {
460+ auto errorMessage = error.getMessage ();
461+ auto errorStack = error.getStack ();
462+ jsi::Value newErrorObj =
463+ runtime.global ()
464+ .getPropertyAsFunction (runtime, " Error" )
465+ .callAsConstructor (
466+ runtime,
467+ jsi::String::createFromUtf8 (runtime, errorMessage));
468+ newErrorObj.getObject (runtime).setProperty (
469+ runtime,
470+ " stack" ,
471+ jsi::String::createFromUtf8 (runtime, errorStack));
472+ jsi::JSError newError (
473+ std::move (newErrorObj), errorMessage, errorStack);
474+ onTaskError_ (runtime, newError);
475+ } catch (...) {
476+ jsi::JSError fallback (runtime, error.getMessage ());
477+ onTaskError_ (runtime, fallback);
478+ }
423479 } catch (std::exception& ex) {
424480 jsi::JSError error (
425481 runtime, std::string (" Non-js exception: " ) + ex.what ());
0 commit comments