ResizeObserver - loop limit exceeded
Asked Answered
M

16

309

About two months ago we started using Rollbar to notify us of various errors in our Web App. Ever since then we have been getting the occasional error:

ResizeObserver loop limit exceeded

The thing that confuses me about this is that we are not using ResizeObserver and I have investigated the only plugin which I thought could possibly be the culprit, namely:

Aurelia Resize

But it doesn't appear to be using ResizeObserver either.

What is also confusing is that these error messages have been occuring since January but ResizeObserver support has only recently been added to Chrome 65.

The browser versions that have been giving us this error are:

  • Chrome: 63.0.3239 (ResizeObserver loop limit exceeded)
  • Chrome: 64.0.3282 (ResizeObserver loop limit exceeded)
  • Edge: 14.14393 (SecurityError)
  • Edge: 15.15063 (SecurityError)

So I was wondering if this could possibly be a browser bug? Or perhaps an error that actually has nothing to do with ResizeObserver?

Montemontefiascone answered 20/3, 2018 at 12:22 Comment(15)
Funny how even the docs say ResizeObserver has a mechanism to avoid infinite callback loops and cyclic dependencies. Did you update the dependency to element-resize-detector (dependency of aurelia-resize) recently? Seems that one had an update in January..Freitag
As a workaround you could do window.ResizeObserver = undefined; at the start of your application to just disable the ResizeObserver. Not the best solution of course, but just reverts it back to what it was when it worked..Freitag
Can you provide a repro of your code, and yes ResizeObserver gives UAs an out (at an unspecified limit) to bail on a loop. The Edge Security Error is going to be completely different as we don't currently support ResizeObserver.Rebus
Hi, thanks for the feedback. Unfortunately I won't be able to share the repro. I am discussing the possible solutions put forward by @FredKleuver with my team.Montemontefiascone
@FredKleuver it appears that the aurelia-resize currently uses and older version of element-resize-detector and hasn't been updated.Montemontefiascone
@FredKleuver we have decided to park this for now since it seems to be an issue possibly related to dependencies and it is a silent error so we would rather not do anything hacky with it unless absolutely necessary.Montemontefiascone
@Montemontefiascone please consider adding your own response reflecting your decision.Flyboat
This error means that ResizeObserver was not able to deliver all observations within a single animation frame. It is benign (your site will not break).Financier
Ah thanks very much! I think the main concern was that it would cause performance issues. I don't work there anymore but I'll let the guys know.Montemontefiascone
@AleksandarTotic It is still strange that we were getting this error from a browser that doesn't support it, and when our app didn't appear to be using it.Montemontefiascone
it's a real PITA if you are trying to use onerror. To make matters worse, safari will prevent the message from showing up so you can't even filter it outAccrete
@AleksandarTotic thank you for the information here! I do have a question though: like with the question author, my hybrid app does not use ResizeObserver anywhere (even searching my entire computer, the only instance of ResizeObserver is a random Steam file). I'm concerned that this is indicative of another issue on the app; what would be the mechanism for this error happening when the ResizeObserver is never used?Stambaugh
> what would be the mechanism for this error happening when the ResizeObserver is never used? This error will never happen if RO is not used. Where is the usage coming from? I see 2 options. A) An extension, or B) RO is used internally by Chrome in some tags (video). B) should not happen, and is a bug. If you have a reproducible case you can share, please file a bug on crbug.com.Financier
@AlexanderTaran we were using a video that was getting a ResizeObserver attached by Chrome; thank you for the help here! I really appreciate itStambaugh
In case you want to explicitly ignore it, you must also do the same thing with Firefox's version of this which is ResizeObserver loop completed with undelivered notifications. (including the period)Hershelhershell
H
425

You can safely ignore this error.

One of the specification authors wrote in a comment to your question but it is not an answer and it is not clear in the comment that the answer is really the most important one in this thread, and the one that made me comfortable to ignore it in our Sentry logs.

This error means that ResizeObserver was not able to deliver all observations within a single animation frame. It is benign (your site will not break). – Aleksandar Totic Apr 15 at 3:14

There are also some related issues to this in the specification repository.

Hurlow answered 17/5, 2018 at 8:53 Comment(9)
Also see Error "ResizeObserver loop limit exceeded" Firing When Video Controls Exist.Magistracy
We had this issue in coming from @microsoft/applicationinsights-web which does our client error logging. So we just ignore this error by setting up an error event handler prior to applicationInsights and call stopImmediatePropagation and preventDefaultMerla
@Merla any thoughts on how to do that on safari? No matter what I only get "script error." when this event happensAccrete
I'm not sure it's always sensible/safe to ignore this error. It can indicate a performance problem, for example: jsbin.com/cadafaduwu/1/edit?html,css,js,console,output. The resize observer callback triggers the element to resize again, thus creating a loop. When this happens, it's usually a mistake—you don't want to trigger another resize from a resize observer callback.Portly
Also, there's a good description of why this error happens here: webkit.org/blog/9997/resizeobserver-in-webkit/…. (In Safari the error message is slightly different but the logic still applies.)Portly
In case it helps anyone understand why they are seeing this error, I created a repository with a bunch of different reduced test cases where you might run into this error: github.com/OliverJAsh/resize-observer-loop-testsPortly
Related ticket, including the error message for Firefox and a little workaround: https://mcmap.net/q/101436/-how-to-ignore-the-quot-resizeobserver-loop-limit-exceeded-quot-in-testcafeNaara
@Merla Facing the same issue. @microsoft/applicationinsights-web is logging "ResizeObserver - loop limit exceeded". Could you please post code snippet that you wrote to suppress this error from @microsoft/applicationinsights-web logs.Wool
Something like this: window.addEventListener('unhandledrejection', e => { if (/resizeobserver/.test(e?.message || e || '').toString()) e.preventDefault(); e.stopImmediatePropagation() })Merla
C
103

It's an old question but it still might be helpful to someone. You can avoid this error by wrapping the callback in requestAnimationFrame. For example:

const resizeObserver = new ResizeObserver(entries => {
   // We wrap it in requestAnimationFrame to avoid this error - ResizeObserver loop limit exceeded
   window.requestAnimationFrame(() => {
     if (!Array.isArray(entries) || !entries.length) {
       return;
     }
     // your code
   });
});
Cathey answered 4/11, 2019 at 21:45 Comment(7)
is the condition needed? "!Array.isArray(entries) || !entries.length"Subject
What do you mean?Cathey
How did you know to do this...?Roemer
@Roemer from a response above: "This error means that ResizeObserver was not able to deliver all observations within a single animation frame". By wrapping it in requestAnimationFrame you can limit the executions to a single frameImpel
You may not want to do this. " you can limit the executions to a single frame" A more accurate explanation is that you're actually pushing the layout change onto the macrotask queue. This is unfortunately an important distinction, because RO normally would run inbetween layout and paint. This is important because it lets you make layout changes without "jank" or "flickering" You can find a great example of that here: github.com/petyosi/react-virtuoso/issues/269 That doesn't mean that you cannot use this solution -- but if you are using RO for sync updates, reconsider.Jagatai
Nice approach, but to make it potentially more beneficial - you are required to call requestAnimationFrame only once. Any further calls could process the array directly without wrapping. I'm not sure though if it gives any particular perf benefits.Penthouse
Note that this trick means your resizes will always be lagging one frame behind, which may not always be what you want.Pinder
C
64

If you're using Cypress and this issue bumps in, you can safely ignore it in Cypress with the following code in support/index.js or commands.ts

const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/
Cypress.on('uncaught:exception', (err) => {
    /* returning false here prevents Cypress from failing the test */
    if (resizeObserverLoopErrRe.test(err.message)) {
        return false
    }
})

You can follow the discussion about it here. As Cypress maintainer themselves proposed this solution, so I believe it'd be safe to do so.

Cardigan answered 21/8, 2020 at 8:34 Comment(3)
Your regex doesn't quite do what you'd expect it to do. The square brackets enclose a character class, which the ^ inside it negates so in reality you're ignoring any error that starts with any of the characters included in the sentence. If you want to use a regex, the negative lookahead is what you're looking for: return /^(?!ResizeObserver loop limit exceeded)/.test(err.message) should be fine. Though err.message.startsWith('ResizeObserver loop limit exceeded') might be easier anyhow.Ferrin
I would even go as far as to state a regular expression is unnecessary for that -- you can just look for the string "ResizeObserver loop limit exceeded" inside the error message, plain and simple.Creativity
I can confirm that if (err.message.includes("ResizeObserver loop limit exceeded")) works as well as the regex.Lantern
B
11

We had this same issue. We found that a chrome extension was the culprit. Specifically, the loom chrome extension was causing the error (or some interaction of our code with loom extension). When we disabled the extension, our app worked.

I would recommend disabling certain extensions/addons to see if one of them might be contributing to the error.

Babbling answered 18/12, 2019 at 0:18 Comment(2)
Or just check in incognito mode. Most people are likely to have either none, or far fewer extensions enabled in incognito.Cyb
+1 worked for me. Im my case, it was "Avira" chrome extension causing the error "ResizeObserver loop limit exceeded"Scheelite
H
10

For Mocha users:

The snippet below overrides the window.onerror hook mocha installs and turns the errors into a warning. https://github.com/mochajs/mocha/blob/667e9a21c10649185e92b319006cea5eb8d61f31/browser-entry.js#L74

// ignore ResizeObserver loop limit exceeded
// this is ok in several scenarios according to 
// https://github.com/WICG/resize-observer/issues/38
before(() => {
  // called before any tests are run
  const e = window.onerror;
  window.onerror = function(err) {
    if(err === 'ResizeObserver loop limit exceeded') {
      console.warn('Ignored: ResizeObserver loop limit exceeded');
      return false;
    } else {
      return e(...arguments);
    }
  }
});

not sure there is a better way..

Henbane answered 4/10, 2020 at 17:19 Comment(4)
This worked for me, except that I had to return true in the if, to prevent the firing of the default event handler (see MDN).Budbudapest
There is a better way to do the same thing -- without window.onerror "acrobatics". Don't use onerror -- use addEventListener with "error" as event type, and stopPropagation to stop propagation of the event as needed. Alternatively, add the event listener on the object you suspect of actually firing the error event, and stop propagation there. This has been covered in detail in answers to related questions.Creativity
Where is arguments coming from?Bibby
@Bibby developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Hendrix
H
9

The error might be worth investigating. It can indicate a problem in your code that can be fixed.

In our case an observed resize of an element triggered a change on the page, which caused a resize of the first element again, which again triggered a change on the page, which again caused a resize of the first element, … You know how this ends.

Essentially we created an infinite loop that could not be fitted into a single animation frame, obviously. We broke it by holding up the change on the page using setTimeout() (although this is not perfect since it may cause some flickering to the users).

So every time ResizeObserver loop limit exceeded emerges in our Sentry now, we look at it as a useful hint and try to find the cause of the problem.

Halliburton answered 16/2, 2022 at 13:6 Comment(2)
Thank you for the answer. This was our case, we have image observer and observer tried send request to the server to get a new image derivative but server responded with 404 and Opera/Chrome adds default icon to indicate a broken image. This icon trigered a new observer call because the size of image changed and we again reset image by setting image.src that causes infinite process. Here some more details dgo.to/easy_responsive_images/3285476 The solution in our case was checking observed image size to detect if this image is not default broken image icon.Natant
Would it be possible to call this.unobserve() before you do your changes in side the ResizeObserver, and then when you are done turn this.observe() to restart? I am running in to this problem too and there is quite some work done before the new final size is determent. Couldn't get this to work though :(Guth
F
6

add debounce like

new ResizeObserver(_.debounce(entries => {}, 200);

fixed this error for me

Faceharden answered 22/9, 2020 at 9:11 Comment(1)
this works but i some cases will cause janky behavior, for example when animating the closing of a panel. setting the wait to 1 seems to have resolved the error for me while keeping the animation smooth.Lacrosse
C
4

One line solution for Cypress. Edit the file support/commands.js with:

Cypress.on(
  'uncaught:exception',
  (err) => !err.message.includes('ResizeObserver loop limit exceeded')
);
Cryogenics answered 3/3, 2022 at 23:3 Comment(0)
S
2

In my case, the issue "ResizeObserver - loop limit exceeded" was triggered because of window.addEventListener("resize" and React's React.useState.

In details, I was working on the hook called useWindowResize where the use case was like this const [windowWidth, windowHeight] = useWindowResize();.

The code reacts on the windowWidth/windowHeight change via the useEffect.

React.useEffect(() => {
    ViewportService.dynamicDimensionControlledBy(
        "height",
        { windowWidth, windowHeight },
        widgetModalRef.current,
        { bottom: chartTitleHeight },
        false,
        ({ h }) => setWidgetHeight(h),
    );
}, [windowWidth, windowHeight, widgetModalRef, chartTitleHeight]);

So any browser window resize caused that issue.

I've found that many similar issues caused because of the connection old-javascript-world (DOM manipulation, browser's events) and the new-javascript-world (React) may be solved by the setTimeout, but I would to avoid it and call it anti-pattern when possible.

So my fix is to wrap the setter method into the setTimeout function.

React.useEffect(() => {
    ViewportService.dynamicDimensionControlledBy(
        "height",
        { windowWidth, windowHeight },
        widgetModalRef.current,
        { bottom: chartTitleHeight },
        false,
        ({ h }) => setTimeout(() => setWidgetHeight(h), 0),
    );
}, [windowWidth, windowHeight, widgetModalRef, chartTitleHeight]);

Spiderwort answered 6/1, 2022 at 6:49 Comment(0)
K
2

We also had that issue with Monaco Editor because it is using ResizeObserver internally.

To fix it, we patched the original API by doing:

class CalmResizeObserver extends ResizeObserver {
  constructor(callback: ResizeObserverCallback) {
    super((entries, observer) => {
      requestAnimationFrame(() => {
        callback(entries, observer);
      });
    });
  }
}

win.ResizeObserver = CalmResizeObserver;
Keloid answered 15/5, 2023 at 13:19 Comment(1)
DO NOT DO THIS. The error is harmless. Just ignore it. This "fix" breaks synchronous rendering. github.com/w3c/csswg-drafts/issues/5488#issuecomment-812279513 > Many folks simply ignore it via RAF / setTimeout by queueing a microtask / microtask but this defeats the purpose -- since you don't get synchronous rendering! For example, see petyosi/react-virtuoso@5475a10Wastage
B
1

https://github1s.com/chromium/chromium/blob/master/third_party/blink/renderer/core/resize_observer/resize_observer_controller.cc#L44-L45 https://github1s.com/chromium/chromium/blob/master/third_party/blink/renderer/core/frame/local_frame_view.cc#L2211-L2212

After looking at the source code, it seems in my case the issue surfaced when the NotifyResizeObservers function was called, and there were no registered observers.

The GatherObservations function will return a min_depth of 4096, in case there are no observers, and in that case, we will get the "ResizeObserver loop limit exceeded" error.

The way I resolved it is to have an observer living throughout the lifecycle of the page.

Bridgehead answered 6/8, 2021 at 17:37 Comment(0)
R
1

In my case, I was only interested in changes to an element's width. But changing the element's height during a ResizeObserver event caused a "ResizeObserver loop limit exceeded" error.

To suppress the error, I stopped observing prior to changing the element's size, and started observing again after rendering was complete. Essentially:

const observer = new ResizeObserver(function (entries) {
    observer.unobserve(element);
    // ...manipulate the element...
    setTimeout(function () {
        observer.observe(element);
    }, 0);
});

The complete solution is here: https://gist.github.com/diachedelic/b026fdd168c8af8cd8ac5cb914e7b3cc.

Rezzani answered 23/3, 2023 at 1:49 Comment(4)
Why do you setTimeout(()=>{}, 0) ?Guth
I just copied your example and put some extra text in the element that is .unobserved(element) and than .observe(element) again. It keeps recalling itself even when I set the timeout to 1000..Guth
What browser are you using? Have you studied the complete solution I linked to?Rezzani
I am testing using cypress in Firefox and Electron. I studied the example again. I added the checks if the width is defined and not equal to the current width. Now the function seems to be called only initially and not on resizes.. I posted a question about it...maybe you would have time to look at it? thank you in advance! https://mcmap.net/q/101437/-javascript-resizeobserver-keeps-on-calling-itself-after-unobserving-the-element/1735311Guth
A
1

Reactjs

Fix Error: ResizeObserver loop limit exceeded

useEffect(() => {
    window.addEventListener('error', e => {enter code here
      if (e.message === 'ResizeObserver loop limit exceeded' || e.message === 'Script error.') {
        const resizeObserverErrDiv = document.getElementById(
          'webpack-dev-server-client-overlay-div'
        )
        const resizeObserverErr = document.getElementById(
          'webpack-dev-server-client-overlay'
        )
        if (resizeObserverErr) {
          resizeObserverErr.setAttribute('style', 'display: none');
        }
        if (resizeObserverErrDiv) {
          resizeObserverErrDiv.setAttribute('style', 'display: none');
        }
      }
    })
  }, [])
Asthenia answered 10/5, 2023 at 9:45 Comment(0)
K
0

Managed to solve this in React for our error logger setup.

The Observer error propagates to the window.onerror error handler, so by storing the original window.onerror in a ref, you can then replace it with a custom method that doesn't throw for this particular error. Other errors are allowed to propagate as normal.

Make sure you reconnect the original onerror in the useEffect cleanup.

const defaultOnErrorFn = useRef(window.onerror);

useEffect(() => {
  window.onerror = (...args) => {
    if (args[0] === 'ResizeObserver loop limit exceeded') {
      return true;
    } else {
      defaultOnErrorFn.current && defaultOnErrorFn.current(...args);
    }
  };
  return () => {
    window.onerror = defaultOnErrorFn.current;
  };
}, []);
Kermit answered 26/5, 2022 at 14:8 Comment(0)
P
0

I had this issue with cypress tests not being able to run. I found that instead of handling the exception the proper way was to edit the tsconfig.json in a way to target the new es6 version like so:

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "baseUrl": "../node_modules",
    "target": "es5", --> old
    "target": "es6", --> new
    "types": ["cypress", "@testing-library/cypress"],
    "sourceMap": true
  },
  "include": [
    "**/*.ts"
  ]
}
Primeval answered 17/10, 2022 at 15:26 Comment(0)
L
0

In Cypress/Jest/Mocha, you can avoid this error with a polyfill:

npm install resize-observer-polyfill --save-dev 

Add this to the test:

global.ResizeObserver = require('resize-observer-polyfill');
Lantern answered 5/5, 2023 at 14:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.