sentry-javascript: Very uninformative onunhandledrejection errors when rejecting with Event

Package + Version

  • @sentry/browser
  • @sentry/node
  • raven-js
  • raven-node (raven for node)
  • other:

Version:

5.6.2

Description

Errors captured through onunhandledrejection mechanism never really provide any useful information that would help in figuring out the problem.

In my app, I have thousands (17K as of now for one of them, after few months) of UnhandledRejection errors. All info that those events provide is just a an error text like UnhandledRejection: "CustomEvent" (note that with latest version of Sentry it became a little bit more informative with UnhandledRejection: Non-Error promise rejection captured with keys: isTrusted text). There is no stack nor extra context and those errors happen on routes and paths that work perfectly for me and everyone I know so I’m unable figure out how to address them.

Now, I assume, that the problem is only with unhandled promise rejections that reject with non-Error type like primitive values (string, null, etc.) or (like in this case) an Event object. While I don’t see how to improve cases with primitive values, I think for event objects, more data could be logged.

It seems the UnhandledRejection: Non-Error promise rejection captured with keys: isTrusted message comes from the fact that isTrusted is the only iterable key on the event object (try console.log(Object.keys(new CustomEvent('test')))). But event objects have more useful (non-iterable) keys that could provide more information about the error. Namely I’m thinking about detail and type properties. I think those could help identifying from which code event is coming from.

So I’m suggesting to add some custom code (if there isn’t already) for handling that special case (unhandledrejection with event object) better.

Some example events: https://sentry.io/share/issue/4d294f75e9704103b7c3b49e62b6e278/ https://sentry.io/share/issue/c36904ab4c224fbda6f1afff63c106ee/ (these are minimal, sharable versions but full events don’t really provide any other meaningful information either.)

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 72
  • Comments: 74 (15 by maintainers)

Most upvoted comments

I’m currently receiving thousands of instances of this error from a relatively small number of users. Since I’m just testing out Sentry right now with a free plan, there’s no way to filter them out. Spike Protection also does not seem to react to this issue.

I scheduled better Event/CustomEvent handling work for the next week. Will keep everyone posted on this one.

@mogelbrod is there a way you can provide a repro-case for this? We never had an issue with TraceKit incorrectly detecting Error instances.

As for your additional enumerables, we have an integration for this – https://docs.sentry.io/platforms/javascript/#extraerrordata

import * as Sentry from '@sentry/browser';
import { ExtraErrorData } from '@sentry/integrations';

Sentry.init({
  integrations: [new Integrations.ExtraErrorData()]
});

is basically equivalent of your beforeSend snippet.

As a user, ideally all extension errors would be filtered out.

It would be amazing if Sentry had a built-in mechanism to also intelligently auto-filter unhandled rejection errors from extensions!

I wonder if these “installed outside of web store” extensions can also be filtered out with the Filter out errors known to be caused by browser extensions checkbox.

Hey, I found where are they coming from in our project. They actuall come from onerror handler for element like <img>. If

new Promise((reslove, reject) => {
  const img = document.createElement('img);
  img.onerror = (e) => reject(e)
})

is used, the rejected e will be an Event type according to https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror

The same apply to XMLHttpRequest.onerror which gives ProgressEvent. There is also XMLHttpRequest.timeout which gives undefined

Also experiencing isTrusted issues.

Updating to the beta version helped, and extra info seems to be serialized & sent correctly.

In our case, the error turned out to originate from a Chrome extension. What’s interesting, we have the Filter out errors known to be caused by browser extensions option turned on, so it seems like the filtering mechanism does not always work.

Raw additional data:

{
   "isTrusted":false,
   "type":"unhandledrejection",
   "target":"[object Window]",
   "currentTarget":"[object Window]",
   "detail":{
      "reason":{
         "message":"Extension context invalidated.",
         "name":"Error",
         "stack":"Error: Extension context invalidated.\n    at Object._updateMetadata (chrome-extension://haldlgldplgnggkjaafhelgiaglafanh/admin.js:9:168287)\n    at chrome-extension://haldlgldplgnggkjaafhelgiaglafanh/admin.js:9:168105\n    at c (chrome-extension://haldlgldplgnggkjaafhelgiaglafanh/admin.js:9:140858)\n    at E._settlePromiseFromHandler (chrome-extension://haldlgldplgnggkjaafhelgiaglafanh/admin.js:9:113655)\n    at E._settlePromiseAt (chrome-extension://haldlgldplgnggkjaafhelgiaglafanh/admin.js:9:114928)\n    at E._settlePromises (chrome-extension://haldlgldplgnggkjaafhelgiaglafanh/admin.js:9:116773)\n    at u._drainQueue (chrome-extension://haldlgldplgnggkjaafhelgiaglafanh/admin.js:9:74164)\n    at u._drainQueues (chrome-extension://haldlgldplgnggkjaafhelgiaglafanh/admin.js:9:74225)\n    at drainQueues (chrome-extension://haldlgldplgnggkjaafhelgiaglafanh/admin.js:9:72536)"
      },
      "promise":{
         "_receiver0":"[undefined]",
         "_promise0":"[undefined]",
         "_bitField":136839168,
         "_progressHandler0":"[undefined]",
         "_settledValue":"[Object]",
         "_fulfillmentHandler0":"[undefined]",
         "_rejectionHandler0":"[undefined]"
      }
   }
}

I believe the haldlgldplgnggkjaafhelgiaglafanh ID is that of an unlisted extension called “GoGuardian Beacon”. See the screenshot in step 12 of Beacon’s installation manual. GoGuardian is a company that makes software tracking students. They may have cloned the HTML Content Blocker extension, rebranding & unlisting their copy for commercial use, which is why HTML Content Blocker’s fingerprint is present instead of Beacon’s.

image

A student on a device with GoGuardian Beacon (force-)installed and using Sentry probably caused the error.

@kiruh it should be easily filtered with per-project setting:

image

Hello @OliverJAsh, we have the same uninformative logs as your case at the serialized section { currentTarget: [object Window], detail: None, isTrusted: [Circular ~], target: [object Window], type: unhandledrejection } . Have you found a solution?

Since 5.7.0 we see a spike in such messages, especially Non-Error exception captured with keys: currentTarget, isTrusted, target, type. Maybe there was a change in the latest release that causes that?

I just rolled out 5.7.0-beta. Would appreciate testing it out via

npm install @sentry/browser@5.7.0-beta.0

It improves events serialization and unifies some differences between reject/onerror/manual error handling. It also removes any dependencies that were necessary for IE10/11, including Promises, so we have to make sure it’s working correctly.

Any feedback appreciated! 😃

@Jun711:

A quick Google search for haldlgldplgnggkjaafhelgiaglafanh yielded a few results indicating this “extension” might indeed be a malicious script.

Notice this:

chrome-extension://haldlgldplgnggkjaafhelgiaglafanh/teacher/lesson-plans/blocked.html?rdr=chrome://newtab/&cs=%5B%7B%22role%22:%22owner%22,%22name%22:%22Charlotte%20Bethany%22,%22isActive%22:true%7D,%
chrome-extension://haldlgldplgnggkjaafhelgiaglafanh/external/lesson-plans/blocked.html?rdr=chrome://newtab/&cs=%5B%7B%22role%22:%22owner%22,%22name%22:%22Sara%20Detmer%22,%22isActive%22:true%7D%5D

It looks like it’s trying to log in as a user with “owner” rights, and the query params always look the same. More than one search result mentions an online learning platform called Go Guardian (see the Google search page above).

Interestingly enough, there is no extension with such an id in the Chrome Store. If you look up any existing Chrome extension by its id, it will pop up in search results.

Wrapping a non-Error in Error was just to get the correct format for compatibility and also typing.

I have experienced several third party libraries that throw objects that have some information about the error but the nature of the error is . It is somewhere between an event and an error.

For instance, Google Analytics throws this object:

 {
    details: Not a valid origin for the client: https://mywebsite.com has not been whitelisted for client ID myid.apps.googleusercontent.com. Please go to https://console.developers.google.com/ and whitelist this origin for your project's client ID.,
     error: idpiframe_initialization_failed
}

Which is an error that I’d like to know about in Sentry and it makes it all of the way to Sentry’s captureException, but google throws it as an object instead of an Error (as far as I can tell), so Sentry doesn’t handle it in the a way that is intuitive to me.

Javascript users should throw Errors, but the language allows them to throw plain objects, and sometimes they do. Those objects often contain error information that is helpful for debugging (the google example tells me everything I need to know) even without a stacktrace

A dynamic version of @rchl 's strategy would make sense to me – iterate through the object keys and attach them to event.extra

@kamilogorek I tried the solution you suggest (implement a beforeSend inside the init) but the error seems to be still sent, yet with fewer information (detail object becomes ‘None’ ). Do you have any other advice to filter out this specific error ? Thank you.

Same issue today with this extension & version 5.15.0 upgraded to 5.15.5: https://sentry.io/share/issue/3461d17193c64e1d980cd6d41a66ba23/

image

Screen Shot 2020-04-28 at 10 30 40 AM @Jun711 @karol-majewski I found the offending extension. It’s called GoGuardian https://www.goguardian.com/ and the extension is installed via their own installer rather than through the Chrome Web Store.

@Jun711

Since there is not such an extension, it could be an unpublished one and gets passed around by file-sharing instead of downloaded from chrome store.

That was my instinct as well. If all the results seem to be connected to one digital product, chances are, this Chrome extension is an add-on to that platform. What’s unclear is why it attempts to do its thing on other origins. Could be malice, could be Hanlon’s razor.

Do you think it successfully logged users into your website?

No. If that’s an attack, then it’s a poor one.

Since 5.7.0 we see a spike in such messages, especially Non-Error exception captured with keys: currentTarget, isTrusted, target, type. Maybe there was a change in the latest release that causes that?

We saw that as well. It’s because more keys are extracted while the isTrusted is the only one that can be deserialized.

Version: 5.15.5

I have tried to reduce an error noise by setting ignoreErrors and blacklistUrls as described here https://docs.sentry.io/platforms/javascript/#decluttering-sentry

Sentry.init({
    dsn,
    ignoreErrors: [
      // Random plugins/extensions
      'top.GLOBALS',
      // See: http://blog.errorception.com/2012/03/tale-of-unfindable-js-error.html
      'originalCreateNotification',
      'canvas.contentDocument',
      'MyApp_RemoveAllHighlights',
      'http://tt.epicplay.com',
      'Can\'t find variable: ZiteReader',
      'jigsaw is not defined',
      'ComboSearch is not defined',
      'http://loading.retry.widdit.com/',
      'atomicFindClose',
      // Facebook borked
      'fb_xd_fragment',
      // ISP "optimizing" proxy - `Cache-Control: no-transform` seems to
      // reduce this. (thanks @acdha)
      // See http://stackoverflow.com/questions/4113268
      'bmi_SafeAddOnload',
      'EBCallBackMessageReceived',
      // See http://toolbar.conduit.com/Developer/HtmlAndGadget/Methods/JSInjection.aspx
      'conduitPage'
    ],
    blacklistUrls: [
      // Facebook flakiness
      /graph\.facebook\.com/i,
      // Facebook blocked
      /connect\.facebook\.net\/en_US\/all\.js/i,
      // Woopra flakiness
      /eatdifferent\.com\.woopra-ns\.com/i,
      /static\.woopra\.com\/js\/woopra\.js/i,
      // Chrome extensions
      /extensions\//i,
      /^chrome:\/\//i,
      // Other plugins
      /127\.0\.0\.1:4001\/isrunning/i,  // Cacaoweb
      /webappstoolbarba\.texthelp\.com\//i,
      /metrics\.itunes\.apple\.com\.edgesuite\.net\//i
    ]
  });

Yet I still receive those errors.

image

Is there anything I can do? Because it’s eating my quota.

I’ve got 75K events from this chrome extension and it ate my quota

image

As this thread got really large. First, please update the version of the SDK to the newest one, and then please open a new issue detailing what, where and how it is happening, so we can start to fix them one by one. Thanks!

@kamilogorek should a new issue be opened?

As this thread got really large. First, please update the version of the SDK to the newest one, and then please open a new issue detailing what, where and how it is happening, so we can start to fix them one by one. Thanks!

@antoinerousseau thanks for clarifying, don’t know how I missed that 😅 I was thinking specifically of these unhandled rejection errors, though. I’ll edit my comment so it’s clear!

@vitorbal there already is such mechanism, please read the thread again, it’s just not always working perfectly https://github.com/getsentry/sentry-javascript/issues/2210#issuecomment-540022105

@kamilogorek IIUC, exceptions and promise rejections both have stacktraces (error.stack, assuming the value is an Error). I understand there are separate mechanisms for handling exceptions and promise rejections, but I don’t see why that means we can’t give them equal treatment?

As a user, ideally all extension errors would be filtered out.

@ajhool yep, I’ll probably do that if the issue persists and I decide to keep using Sentry. I thought it might be helpful to report the impact to this Issue discussion.

I’m experiencing this, too, and I think that Sentry needs to handle non-Error types properly, rather than treating them like an error.

I’m still working through the problem and I tried adding this code to wrap objects in an Error type, but this didn’t fix the problem:

  const wrappedError = (err instanceof Error || typeof err === 'string') ? err : new Error(JSON.stringify(err));
  Sentry.captureException(wrappedError);

Next, I’m going to try using Sentry.captureMessage if the instance type is not an Error, but a lot of third party libraries throw non-Error types that should be treated as Errors in Sentry IMO

I would actually disagree with the title of this issue – I don’t think that Sentry is being uninformative, I think it’s actually informing developers that Sentry doesn’t properly handle some types of common javascript errors (ie. thrown objects that aren’t of instance type: Error). I see this as being an actual bug in Sentry, because a thrown Object in javascript is still an Exception/Error, even if the thrown object is not of Error type IMO. I’m not sure where the javascript community would stand on this, admittedly (https://stackoverflow.com/questions/9156176/what-is-the-difference-between-throw-new-error-and-throw-someobject)