Go to main content
July 13, 2025
Cover image

Sending errors to an error management application is crucial for identifying and fixing problems in your application. Tools like Sentry, Rollbar, and Datadog play a vital role in this process. However, these tools can sometimes capture errors that are unrelated to your application, such as those caused by browser plugins.

In a previous article, No more sentry Alert? Discover 2 Culprits I Encountered I explored how Sentry overrides addEventListener to catch errors that are thrown. To elaborate further, this article will delve into the thirdPartyErrorFilterIntegration, which helps filter out these irrelevant errors.

We will focus on how this integration works, specifically examining @sentry/core@9.15.0 and @sentry/webpack-plugin. Let’s dive deep into the mechanics of this integration and understand how it can enhance your error monitoring process.


A stack trace provides a snapshot of the call stack at a specific point in time when an error occurs. Essentially, it tells you:

An error happened here, and here’s how the code execution arrived at this point.

Let’s examine a typical stack trace and identify its key components:

Error: Example error message at functionName (fileName.js:10:15) at anotherFunction (anotherFile.js:20:25) ...
  • Error message: This describes the type of error that occurred.
  • Frames: Each line in the stack trace represents a frame, showing a function call in the execution path.
  • Function name: The name of the function where the error occurred or which was being executed.
  • File name: The name of the file containing the code where the error was thrown.
  • Line number: The line number in the file where the error occurred.
  • Column number: The column number in the line where the error occurred.
(Hover or click on names to see the corresponding part on the stack trace)

The configuration of the thirdPartyErrorFilterIntegration occurs at two stages: during the build process (bundle time) and at runtime.

During build process, the integration injects a special code into your build files. This code helps identify which parts of the stack trace belong to your application and which are from third-party libraries. This step is essential for accurately filtering errors later on.

I chose a Vite project as an example of how you can configure the bundler plugin:

// vite.config.ts
import { defineConfig } from "vite";
import { sentryVitePlugin } from "@sentry/vite-plugin";

export default defineConfig({
  plugins: [
    sentryVitePlugin({
      // Use an application key to uniquely identify your application's code
      applicationKey: "your-custom-application-key",
    }),
  ],
});

In this configuration, the applicationKey is a unique identifier for your application. This key is used to mark your application’s code in the stack trace, making it easier to distinguish from third-party code.


At runtime, the integration filters the frames associated with third-party libraries thanks to a filterKeys that you need to configure on the integration. It needs to match the applicationKey injected at build time.

Here’s how you can configure the runtime settings:

Sentry.init({
  integrations: [
    Sentry.thirdPartyErrorFilterIntegration({
      // Ensure this matches the applicationKey used in the bundler configuration
      filterKeys: ["your-custom-application-key"],

      // Define how to handle errors containing third-party stack frames
      // Options include:
      // - 'drop-error-if-contains-third-party-frames':
      //       Drops errors with any third-party frames
      // - 'drop-error-if-exclusively-contains-third-party-frames':
      //       Drops errors only if all frames are third-party
      // - 'apply-tag-if-contains-third-party-frames':
      //       Tags errors containing any third-party frames
      // - 'apply-tag-if-exclusively-contains-third-party-frames':
      //       Tags errors only if all frames are third-party
      behaviour:
        "drop-error-if-contains-third-party-frames",
    }),
  ],
});

As soon as you configure an applicationKey or moduleMetadata function to your bundle plugin configuration, the plugin will inject some code to register some metadata for the current file.

We can see that the applicationKey is injected in some metadata:

const metadata: Record<string, unknown> = {};

metadata[`_sentryBundlerPluginAppKey:${options.applicationKey}`] = true;

The code that is injected is the following one:

// Scope all the code
{
  // Get the global variable
  let _sentryModuleMetadataGlobal =
    typeof window !== "undefined"
      ? window
      : typeof global !== "undefined"
        ? global
        : typeof globalThis !== "undefined"
          ? globalThis
          : typeof self !== "undefined"
            ? self
            : {};

  // Get `_sentryModuleMetadata` and create it if it does not exist
  _sentryModuleMetadataGlobal._sentryModuleMetadata =
    _sentryModuleMetadataGlobal._sentryModuleMetadata || {};

  const stack = new _sentryModuleMetadataGlobal.Error()
    .stack;

  // Put the metadata on the object identified by a fancy and smart identifier
  _sentryModuleMetadataGlobal._sentryModuleMetadata[stack] =
    Object.assign(
      {},
      // We can see that we merge with the potential already existing metadata
      _sentryModuleMetadataGlobal._sentryModuleMetadata[
        stack
      ],
      JSON.stringify(metadata),
    );
}

If you’re curious about your application, you can inspect this object to see its exact contents: console.log(window._sentryModuleMetadata)

For example, on my Astro site I have:

Personal site sentry metadata value


What the heck is new Error().stack?

It’s a trick that allows us to identify the file. Later, we will parse the stack to extract information like the file name.

console.log(new Error().stack);
// It will log
// Error
//   at fileName.js:1:13

Sentry is exporting a function captureException when you want to throw an error to your project dashboard. This is the function that is automatically called in the override of addEventListener by sentry. See my article for more information.

In this function, the Exception is transformed to an Event with the simplified shape:

interface Event {
  exception?: Exception;
}

interface Exception {
  stacktrace?: Stacktrace;
}

interface Stacktrace {
  frames?: StackFrame[];
}

interface StackFrame {
  filename?: string;
  module_metadata?: any;
}

Sentry is using some parsers to transform the stacktrace into the event shape.

const chromeRegex =
  /^\s*at (?:(.+?\)(?: \[.+\])?|.*?) ?\((?:address at )?)?(?:async )?((?:<anonymous>|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;

const geckoREgex =
  /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:[-a-z]+)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;

Once an exception is transformed into an event, integrations that need to enrich or prevent sending it to the project dashboard can run their own processors. Specifically, the thirdPartyErrorFilterIntegration processes the event to enhance and filter the data before it reaches the project dashboard. This involves two main steps:

  • Enrichment with frames metadata: The event is enriched with metadata from the stack frames, providing additional context and details about the error.
  • Filtering non-application errors: The event is filtered to ensure that only errors originating from the application are reported, excluding those from third-party libraries or irrelevant sources.

Let’s explore each of these steps in detail.

During the event processing phase, each frame of the exception is examined and enriched with metadata. This enrichment process leverages the globalThis._sentryModuleMetadata object, which has the following structure: Record<Stack, Metadata>.

The first step in this process is to parse the stack trace to extract the file name from each frame. Once the file name is obtained, adding metadata to each frame becomes straightforward. Here’s a simple example of how this can be achieved:

declare const metadataByFileName: Map<string, Metadata>;

for (const frame of frames) {
  // Retrieve metadata using the frame's file name
  const metadata = metadataByFileName.get(frame.filename);

  // Skip frames without associated metadata
  if (!metadata) {
    continue;
  }

  // Enrich the frame with the retrieved metadata
  frame.module_metadata = metadata;
}

After enriching the frames with metadata, the next step is to determine if the exception is valid based on the behaviour setting configured on the Sentry client. This filtering process involves looping through each frame to retrieve the application key defined in the bundler plugin configuration.

Depending on the specified behaviour, the filtering logic applies different criteria:

  • At least one frame from the application: If the behaviour requires that at least one frame originates from the application, the Array.prototype.some method is used.
  • All frames from the application: If the behaviour requires that every frame in the stack trace comes from the application, the Array.prototype.every method is used.

The simplified example below shows how this filtering might be implemented:

// The same constant that we can find in the bundler plugin
const BUNDLER_PLUGIN_APP_KEY_PREFIX =
  "_sentryBundlerPluginAppKey:";

function getApplicationKeys(frames: StackFrame[]) {
  return frames
    .filter((frame) => !!frame.filename)
    .map((frame) => {
      if (frame.module_metadata) {
        return (
          Object.keys(frame.module_metadata)
            // Find the key corresponding to the applicationKey
            .filter((key) =>
              key.startsWith(BUNDLER_PLUGIN_APP_KEY_PREFIX),
            )
            // Extract the applicationKey value
            .map((key) =>
              key.slice(
                BUNDLER_PLUGIN_APP_KEY_PREFIX.length,
              ),
            )
        );
      }
      // No metadata, probably a frame not corresponding to the application
      return [];
    });
}

function thirdPartyErrorFilterIntegrationProcess(
  event: Event,
) {
  const frames = getFrames(event);

  // `shouldMatchEveryFrame` is calculated from `integrationOptions.behaviour`
  const arrayMethod = shouldMatchEveryFrame
    ? "every"
    : "some";

  const isInvalid = getApplicationKeys(frames)[arrayMethod](
    (applicationKeys) =>
      !applicationKeys.some((key) =>
        integrationOptions.filterKeys.includes(key),
      ),
  );

  if (isInvalid) {
    return null;
  }

  return event;
}

Sentry’s thirdPartyErrorFilterIntegration enhances error tracking by injecting metadata during build time and filtering errors at runtime, ensuring only relevant application errors are reported.

By injecting an applicationKey during the build process and using filterKeys at runtime, this integration effectively distinguishes between application and third-party errors. It enriches stack traces with metadata and filters out non-application errors based on configurable behaviors, allowing developers to focus on the errors that truly matter.

If you are working on a micro-frontend environment, make sure to use the same applicationKey for all your applications or include all of them in the filterKeys.


You can find me on Twitter if you want to comment this post or just contact me. Feel free to buy me a coffee if you like the content and encourage me.