Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instrumenting EventTarget.prototype.addEventListener leads to problems in Chrome #2074

Closed
4 of 5 tasks
ffxsam opened this issue May 14, 2019 · 28 comments · Fixed by #2078, #3208 or #4695
Closed
4 of 5 tasks

Instrumenting EventTarget.prototype.addEventListener leads to problems in Chrome #2074

ffxsam opened this issue May 14, 2019 · 28 comments · Fixed by #2078, #3208 or #4695

Comments

@ffxsam
Copy link

ffxsam commented May 14, 2019

Package + Version

  • @sentry/browser 5.2.0

Description

We're getting this error:

Cannot convert undefined or null to object

It seems to be happening here, according to Chrome:

https://github.com/getsentry/sentry-javascript/blob/master/packages/browser/src/integrations/trycatch.ts#L88

Here's the stack trace:

Screen Shot 2019-05-14 at 10 54 20 AM

Here's a snapshot of the error in Chrome DevTools:

Screen Shot 2019-05-14 at 10 56 18 AM

Seems like OpenLayers is throwing an error, but Sentry is not handling it properly and throwing an error itself.

@BenoitZugmeyer
Copy link

BenoitZugmeyer commented May 16, 2019

I'm stumbling across this error as well. This occurs sometimes when calling the global function addEventListener or removeEventListener, ex:

addEventListener("someevent", callback)

Calling it with a "this parameter" resolves the issue:

window.addEventListener("someevent", callback)

Sadly, I don't reproduce it consistently. The only thing I can think of is that in the first case, this is undefined (because of the "use strict" mode), and in the later case, this would be window.

I am using @sentry/browser@4.4.2. It seems to occur only on recent Chrome / Chromium versions (74.0.3729 and 75.0.3770, see details below). The "fun" fact is: this error is reported quite often so this is the main reason my Sentry event quota is exhausted.

EDIT: joining a screenshot of the detailed list of impacted browsers Screenshot_20190516_194938
As you can see, this seems to start from Chrome 74.0.3729, which has been out since april 23.

@ffxsam
Copy link
Author

ffxsam commented May 16, 2019

This is only happening for us when we're using OpenLayers to create a new map. As a result, I've decided to ditch OL for Leaflet (probably a good move in general), and I'm just hoping this solves my issue.

@BenoitZugmeyer
Copy link

I managed to write a minimal program to reproduce this consistently. Just run it in your console devtools:

const original = EventTarget.prototype.addEventListener
EventTarget.prototype.addEventListener = function (eventName, fn, options) {
  "use strict"
  original.call(this, eventName, fn, options)
}

for (var i = 0; i < 100000; i += 1) {
  if (i % 100 === 0) console.log(i)
  addEventListener("mouseup", () => {})
}

In my case, it throws after the 4600th iteration.

@ffxsam
Copy link
Author

ffxsam commented May 16, 2019

On a semi-related note, when I remove Sentry, LogRocket trips up in a very similar way.

@kamilogorek
Copy link
Contributor

kamilogorek commented May 17, 2019

@BenoitZugmeyer @ffxsam thanks for the repro case. After further investigation, I can tell that it's not our SDK issue, it's just Chrome's limit for event listeners per object.

When you call the repro above on any page (be it http://example.com) it will break as well. No SDK required.

listeners-repro

@BenoitZugmeyer
Copy link

Yes, this is a Chrome issue. But incuding your SDK triggers that issue, breaking our code. Lucky for me this is easily fixable in my codebase, but I would be a bit annoyed if it occured in a dependency like @ffxsam

@kamilogorek
Copy link
Contributor

kamilogorek commented May 17, 2019

@BenoitZugmeyer how so? We don't add our own listeners. We only intercept user's calls to addEventListener. If you don't call it, we won't as well.
And the issue is not triggered by us, but it's reported from our codebase, as this is the place where original invocation ends up.

@BenoitZugmeyer
Copy link

Basically, in your SDK, you are doing this to intercept addEventListener calls:

const original = EventTarget.prototype.addEventListener
EventTarget.prototype.addEventListener = function (eventName, fn, options) {
  "use strict"
  original.call(this, eventName, fn, options)
}

Removing those lines from the snippet I gave you (meaning: removing your SDK in my code) fixes the issue: we can add as many event listeners as needed. So of course you don't add listeners on you own, but the Chrome bug is triggered by the way you are intercepting addEventListener calls.

@kamilogorek kamilogorek reopened this May 17, 2019
@kamilogorek
Copy link
Contributor

kamilogorek commented May 17, 2019

@BenoitZugmeyer you are right here, my bad. It's because of mentioned strict mode. The only thing we can do here is disable, and then it all works just fine 🤷‍♂

const original = EventTarget.prototype.addEventListener
EventTarget.prototype.addEventListener = function (eventName, fn, options) {
  original.call(this, eventName, fn, options)
}

for (var i = 0; i < 100000; i += 1) {
  if (i % 100 === 0) console.log(i)
  addEventListener("mouseup", () => {})
}

@BenoitZugmeyer
Copy link

I guess you could also do something like original.call(this || window, ...). Strict mode is not added in the function like in my snippet, but in the dist files generated by your build setup.

@kamilogorek
Copy link
Contributor

kamilogorek commented May 17, 2019

this || window would effectively alter developers code behavior, so I'm not sure about this one.
use strict is function scoped, and because bundle.js defines it at the very top (well, TypeScript does that for us), it also affects wrapping method mentioned above:

/*! @sentry/browser 5.2.1 (a5b87c1e) | https://github.com/getsentry/sentry-javascript */
var Sentry = (function (exports) {
    'use strict';

Although this || window fixes the issue so... :S and it's like 99.9% usecase that it's coming from window's proto anyway.

@BenoitZugmeyer
Copy link

I reported the issue here: https://bugs.chromium.org/p/chromium/issues/detail?id=964249

I think it wouldn't alter the code behavior because calling the original addEventListener function with undefined seems equivalent to calling it with the global object. But I may not think about all the cases there...

@kamilogorek
Copy link
Contributor

Awesome, thanks! And in the meantime #2078

@futpib
Copy link

futpib commented May 17, 2019

EDIT: found a cleaner workaround:

Sentry.init({
	...
	// XXX: Workaround for https://github.com/getsentry/sentry-javascript/issues/2074
	defaultIntegrations: Sentry.defaultIntegrations.filter(({ name }) => name !== 'TryCatch'),
});
old stuff

I'm opting for this workaround for now.

EventTarget.prototype.addEventListener = EventTarget.prototype.addEventListener.__sentry_original__;
EventTarget.prototype.removeEventListener = EventTarget.prototype.removeEventListener.__sentry_original__;

@javan
Copy link

javan commented May 20, 2019

This issue is causing a fair amount of noise for us: Tens of thousands of events per hour, ~1.8 million total. Looking forward to a fix. 😬

Screen Shot 2019-05-20 at 11 22 24 AM

@kamilogorek
Copy link
Contributor

@javan fixed pending PR tests, we will release patch this week.

@futpib
Copy link

futpib commented May 24, 2019

I'm still seeing this on @sentry/browser@5.3.0.

Running this snippet in the console still throws when sentry is enabled.

for (var i = 0; i < 100000; i += 1) {
  if (i % 100 === 0) console.log(i)
  addEventListener("mouseup", () => {})
}

@javan
Copy link

javan commented May 25, 2019

Same. Issue is still present in v5.3.0.

Example: https://sentry-browser-chrome-bug.glitch.me
Source: https://glitch.com/edit/#!/sentry-browser-chrome-bug?path=src/main.js:18:0

Screenshot

Screen Shot 2019-05-24 at 8 39 53 PM

@javan
Copy link

javan commented May 28, 2019

It appears that using apply() instead of call() works around the issue. Not sure if it's a viable solution for Sentry's implementation.

For example, this snippet tickles the Chrome bug:

const original = EventTarget.prototype.addEventListener
EventTarget.prototype.addEventListener = function(eventName, fn, options) {
  "use strict"
  original.call(this, eventName, fn, options)
}

let count = 0
while (count++ < 10000) addEventListener("click", console.log)

This one does not:

const original = EventTarget.prototype.addEventListener
EventTarget.prototype.addEventListener = function() {
  "use strict"
  original.apply(this, arguments)
}

let count = 0
while (count++ < 10000) addEventListener("click", console.log)

@javan
Copy link

javan commented May 28, 2019

Also, @kamilogorek, I think this issue should be reopened.

@kamilogorek
Copy link
Contributor

Done. Although I'll need some time to take a look at it, as I'm busy with other work. Any help appreciated :)

@rhcarvalho
Copy link
Contributor

May 17, 2019
I reported the issue here: https://bugs.chromium.org/p/chromium/issues/detail?id=964249

May 23, 2019
The Chromium issue has been marked as a duplicate of https://bugs.chromium.org/p/chromium/issues/detail?id=961199, which has been marked as fixed on the same day.

March 13, 2020
I wonder if this is still an issue with recent versions of Chrome/Chromium and the Sentry JS SDK?

@rhcarvalho rhcarvalho changed the title Cannot convert undefined or null to object Instrumenting EventTarget.prototype.addEventListener leads to problems in Chrome Mar 13, 2020
@rhcarvalho
Copy link
Contributor

I haven't found any relevant uses of "use strict" in master, I imagine the implementation might have changed quite a bit since the problem was reported. Marking this a needs-repro for the time being.

@Fonger
Copy link

Fonger commented Sep 2, 2020

@rhcarvalho

We still see many errors coming from Chrome 74 Mobile.

Interestingly, there's no issue with bundle.min.js (from sentrycdn)
We start to see this issue when we switch to bundle.es6.min.js

I guess babel transpile somehow eliminate the usage of buggy function call in Chrome 74 somehow

Our hole website only targets es6 (es2015) so we hope to use es6 build

image

@kamilogorek
Copy link
Contributor

kamilogorek commented Sep 2, 2020

@Fonger can you provide a link to the affected event ever here or directly to kamil@sentry.io? Thanks

@Fonger
Copy link

Fonger commented Sep 2, 2020

@kamilogorek sure. just emailed

We had rollbacked to use bundle.min.js several minutes ago though.

@SaraVieira
Copy link

SaraVieira commented Jan 5, 2021

Hey!

Sorry to do a plus one on this but seems we are also encountering this at codesandbox and its been also in the bunches:

103711776-6ad73e80-4fb8-11eb-86b7-e902874e30e3

Interesting thing is that it seems to only happen on chrome on a mac

You can see the user created crash report here: codesandbox/codesandbox-client#5326

Let me know if you need anything else or any help in this

@kamilogorek
Copy link
Contributor

Cross-posting for the reference: codesandbox/codesandbox-client#5406 (comment)

lobsterkatie added a commit that referenced this issue Mar 8, 2022
…on (#4695)

Users have reported running into a bug in Chrome wherein calling `addEventListener` or `requestAnimationFrame` too many times on `window` eventually throws an error when our `try-catch` integration is running, specifically because of how we wrap those functions. In the discussion of that bug, [one user](#2074 (comment)) reported that replacing our `call` call with an `apply` call in our wrapping functions solved the problem for him.

This makes that change, in the hopes it will fix the problem for everyone.

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