Skip to content

Auto-detect TurboModules on the New Architecture #6898

Open

Description

@sentry-junior

turboModuleContextIntegration is on by default (enableNative), but on the New Architecture it only wraps RNSentry, which is in ignoreTurboModules by default. The default setup therefore records no statistics, span attributes, or slow-call breadcrumbs, and crash context only covers crashes during the SDK's own calls. To get data, users have to list every module in modules: [...].

Current behavior

  • Verified in the RN sample (iOS, New Architecture, RN 0.87.1): the TurboModule Playground span has all turbo_module.* attributes when the sample's modules are passed in modules, and none with the default turboModuleContextIntegration().
  • Automatic instrumentation exists only on the Old Architecture (enableLegacyNativeModules: true), which is gone as of RN 0.82. The automatic path is on the architecture apps are leaving; the manual path is on the one they're moving to.

Why manual registration falls short

  • Third-party libraries resolve their modules internally, so users usually don't have the module objects to pass in.
  • Libraries that copy methods out at import time (const { foo } = NativeX) keep the unwrapped functions.
  • The JS wrapper writes the turbo_module context and two tags to the scope at the start and end of every call. With scope sync on, each write is a native call (scopeSync.ts), so each instrumented call costs about 6 extra native calls.

RN 0.87 module resolution constraints

  • Bridgeless mode (the default) has no global.__turboModuleProxy; TurboModuleRegistry.get() falls back to NativeModules[name], which reads from the read-only global.nativeModuleProxy.
  • getModule() caches one JS object per module name, so in-place wrapping reaches other users of the same module, but the cache is a weak reference, so a wrapper must hold strong references.
  • JS can't enumerate registered modules, and calling get on every name would eagerly load all modules (some with side effects).
  • Libraries usually look up their module at import time, before Sentry.init.

Proposed direction (from team discussion)

  • Options considered: patching TurboModuleRegistry.get/getEnforcing at init (misses lookups before init, not recommended), a Metro resolver redirect via withSentryConfig (skipped: depends on RN internal paths, keeps JS per-call overhead, misses JSI host objects), and native TurboModulePerfLog.
  • Aleksandr Pantiukhov favors the native TurboModulePerfLog route, since it builds on the existing SentryTurboModulePerfSink.h sink interface and needs no new install path. Prior work: Wire TurboModulePerfLogger on iOS and Android #6162.
  • Native crash attribution can be tracked separately with the Cocoa and Java SDK teams; the rest of the work doesn't depend on it.

Raised by Antonis Lilis while reviewing the docs (getsentry/sentry-docs#19949) and changelog (getsentry/sentry-changelog#224) PRs. Related GitHub issue for the Linear bug: #6886; Linear RN-824.

via Antonis Lilis.

--

View Junior Session [Sentry]

Activity

  1. linear-code commented on Oct 16, 2026

    @linear-code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions