Skip to content

[Worklets] evalWithSourceMap still crashes on empty source-map strings after #9607 (debug app + release OTA bundle) #9906

Description

@henry-dorsia

Description

Follow-up to #9600 (closed) and its fix #9607. The UnpackerLoader guard shipped in #9607 does not fully close the crash: a debug native build (Expo dev client) loading a release-transformed bundle (EAS Update / expo export) can still abort at startup with

Error: Exception in HostFunction: Error parsing source map:1:1: JSON object or array expected
    at evalWithSourceMap (native)

We hit this in production tooling (Dorsia member app, preview dev-client channel) on react-native-worklets 0.10.0 — a version that already contains the #9607 guard (UnpackerLoader.huseSourceMaps = !valueUnpacker_.sourceMap.empty()), so #9607 alone demonstrably does not cover all paths. Verified the situation is unchanged in 0.10.2 and on current main.

Why #9607 is incomplete

Two call paths can still hand an empty string to the evalWithSourceMap host function, which passes it to HermesRuntime::evaluateJavaScriptWithSourceMap, where Hermes rejects '' as invalid source-map JSON:

  1. UnpackerLoader.h guard only samples one unpacker. useSourceMaps is derived from valueUnpacker_.sourceMap alone. If valueUnpacker_ has a non-empty map while any other unpacker (synchronizable / customSerializable / shareableHost / shareableGuest / remoteFunction) carries an empty one — e.g. mixed debug/release metadata after the flavor-detection issue fixed in fix(Worklets): Babel plugin not detecting production environment #9609, or partially transformed bundles — evalWithSourceMap is still called with '' for those.

  2. valueUnpacker.native.js per-worklet eval is unguarded. It calls globalThis.evalWithSourceMap('(' + initData.code + '\n)', initData.location, initData.sourceMap) whenever the global exists, without checking initData.sourceMap. undefined happens to be safe (fails the isString() check in the host function), but an empty string crashes. Unchanged on main today.

Suggested fix

Guard at the single native boundary all call paths share — the evalWithSourceMap host function in WorkletHermesRuntime.cpp — by treating an empty string the same as an absent source map:

std::shared_ptr<const jsi::Buffer> sourceMap;
if (count > 2 && args[2].isString()) {
  auto sourceMapString = args[2].asString(rt).utf8(rt);
  // Hermes treats an empty string as invalid source-map JSON. Release
  // worklet transforms may omit source maps, so treat empty strings the
  // same as absent source maps.
  if (!sourceMapString.empty()) {
    sourceMap = std::make_shared<const jsi::StringBuffer>(std::move(sourceMapString));
  }
}

We have been shipping exactly this as a local patch on 0.10.0–0.10.2 and it resolves the startup crash for every caller (unpackers, per-worklet evals, custom serializables) without affecting the debugger's source-map behaviour when a real map is present. Happy to open a PR if this direction is acceptable.

Steps to reproduce

Same shape as #9600 (its repro https://github.com/bj97301/worklets-ota-sourcemap-repro applies):

  1. Debug native build with expo-dev-client
  2. Publish a release-transformed bundle (eas update or expo export) where at least one worklet __initData.sourceMap is an empty string
  3. Launch the dev client and load the OTA bundle → startup crash in evalWithSourceMap

Versions

  • react-native-worklets: 0.10.0 (crash observed), 0.10.2 and main (code paths verified still unguarded)
  • react-native-reanimated: 4.5.0 / 4.5.1
  • React Native: 0.85.3 / 0.86.0, Hermes, New Architecture
  • Workflow: Expo dev client (SDK 56/57), EAS Update
  • Platforms: iOS (observed), Android (same code path)
  • Build type: Debug app + production bundle

Metadata

Metadata

Assignees

No one assigned

    Labels

    Missing infoThe user didn't precise the problem enoughMissing reproThis issue need minimum repro scenarioPlatform: AndroidThis issue is specific to AndroidPlatform: iOSThis issue is specific to iOS

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions