Skip to content

Add configuration option to disable lazy loading of session replay recorder module #3939

Open

Description

@asif-st-republic

Problem

The Datadog RUM SDK lazy-loads the session replay recorder module using dynamic import(), which causes issues in environments that don't support code splitting or dynamic chunk loading, such as:

  • Rails asset pipeline (Sprockets)
  • Legacy webpack configurations without chunkFilename configured
  • Build systems that require all assets to be declared in a manifest

When session replay is enabled (sessionReplaySampleRate > 0), the SDK attempts to fetch the recorder module at runtime:

GET /assets/startRecording

This results in a 404 error and session replay fails to initialize:

Datadog Browser SDK: Recorder failed to start: an error occurred while initializing the module: 
TypeError: Failed to fetch dynamically imported module: https://example.com/assets/startRecording

Requested Feature

Add a configuration option to the datadogRum.init() call that statically bundles the recorder module instead of lazy-loading it:

datadogRum.init({
  applicationId: 'xxx',
  clientToken: 'xxx',
  site: 'datadoghq.com',
  service: 'my-app',
  sessionReplaySampleRate: 100,
  // NEW OPTION:
  bundleRecorderModule: true // or lazyLoadRecorder: false
})

When this option is enabled, the SDK should:

  1. Import the recorder module statically at build time
  2. Skip the dynamic import() call
  3. Use the bundled recorder directly

Workarounds

Currently, the only workarounds are:

  1. Disable session replay entirely (sessionReplaySampleRate: 0)
  2. Suppress console errors (poor UX)
  3. Migrate to a different build system (significant effort)

Environment

  • Browser: All
  • SDK Version: @datadog/browser-rum 6.23.0
  • Build System: Rails asset pipeline (Sprockets)
  • Webpack: 4.x without code splitting configured

Impact

This affects any application using Rails, Django, or other traditional server-rendered frameworks with asset pipelines that don't support dynamic module loading.

Activity

  1. amortemousque commented on Nov 25, 2025

    @amortemousque
    Collaborator

    Hello @asif-st-republic ,

    Thanks for reporting this. For context, we added code-splitting to reduce the SDK overhead on the host app. The browser now downloads only what it needs, which improves load time. Most bundlers support code-splitting today, so we rely on it.

    In your case, Webpack 4 supports code-splitting as well (see doc). Can you explain how you disabled code-splitting and why, so we can help you? If you prefer not to post your config here, you can contact support.

    You can also choose one of these options:

    • Use the CDN setup described in our docs.
    • Stay on the v5 NPM version.
  2. asif-st-republic commented on Dec 15, 2025

    @asif-st-republic
    Author

    Hi @amortemousque , thanks for the reply and the context — totally understand the motivation behind code-splitting and reducing SDK overhead 👍

    To clarify our setup: this is a Rails + Sprockets asset pipeline application using Webpack 4, and the system itself is quite old / legacy, which largely explains why the asset setup looks the way it does today. In this environment, all JS assets must be emitted as concrete files and declared in the asset manifest. Because of that, we intentionally avoid runtime-loaded JS chunks via import() and try to keep changes to the bundling setup as minimal as possible, given the size, age, and complexity of the existing configuration.

    Below is a minimal, anonymized excerpt from our Webpack config showing the relevant constraints. This is taken from a much larger (~346 line) configuration; I’ve only included the parts that affect dynamic chunk loading:

    // Excerpt from a larger (~346 line) webpack config.
    // Showing only the relevant output / chunking constraints.
    
    module.exports = {
      output: {
        // Assets are emitted into Rails' asset pipeline and served from /assets
        path: path.resolve(__dirname, 'vendor/assets/javascripts'),
        filename: '[name].js',
    
        // We intentionally do NOT configure JS chunkFilename / publicPath
        // because all assets must be present and declared ahead of time.
        // chunkFilename: '[name]-[contenthash].js',
        // publicPath: '/assets/',
      },
    
      // Code splitting is enabled only for a limited set of *initial* entrypoints.
      // We do not support general async chunk loading at runtime.
      optimization: {
        splitChunks: {
          cacheGroups: {
            vendor: {
              test: /node_modules/,
              chunks: 'initial',
              name: 'vendor',
              enforce: true,
              chunks: (chunk) => {
                const targets = [
                  'bundles/header',
                  'bundles/footer',
                  'bundles/banner',
                  'bundles/cookie',
                  'share_lots/payments',
                ];
                return chunk.name && targets.some((t) => new RegExp(t, 'i').test(chunk.name));
              },
            },
          },
        },
      },
    };
    
    

    Because of this setup, when the RUM SDK tries to lazy-load the session replay recorder via import(), the browser ends up requesting a separate module URL (e.g. /assets/startRecording), which does not exist in the Sprockets manifest. This results in a 404 and prevents session replay from initializing.

    Our initial goal was to avoid making invasive changes to the existing Webpack/Rails setup just to accommodate this, especially given how old and widely-used the system is.

    In the end, we did move forward with the CDN setup, which works for us operationally. That said, if there is a reasonably simple way to make the npm package work in this kind of legacy asset-pipeline environment, we’d still be very interested — both as a cleaner solution and as a learning opportunity for me.

    Thanks again for the help and for taking the time to look into this 🙏

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions