299

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?

IOIIOOIO
  • 3,899
  • 4
  • 14
  • 19
  • 2
    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.. – Fred Kleuver Mar 20 '18 at 13:55
  • 4
    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.. – Fred Kleuver Mar 20 '18 at 13:59
  • 1
    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. – gregwhitworth Mar 20 '18 at 20:56
  • 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. – IOIIOOIO Mar 22 '18 at 11:29
  • @FredKleuver it appears that the `aurelia-resize` currently uses and older version of `element-resize-detector` and hasn't been updated. – IOIIOOIO Mar 22 '18 at 11:37
  • @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. – IOIIOOIO Mar 22 '18 at 11:39
  • 1
    @IOIIOOIO please consider adding your own response reflecting your decision. – Alexander Taran Mar 23 '18 at 10:55
  • 147
    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 '18 at 03:14
  • 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. – IOIIOOIO Apr 15 '18 at 13:04
  • @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. – IOIIOOIO Apr 15 '18 at 23:22
  • 1
    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 out – NSjonas Aug 21 '20 at 19:00
  • @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? – Sergio Prado Aug 28 '20 at 23:35
  • 2
    > 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. – Aleksandar Totic Aug 30 '20 at 18:00
  • @AlexanderTaran we were using a video that was getting a ResizeObserver attached by Chrome; thank you for the help here! I really appreciate it – Sergio Prado Sep 04 '20 at 18:54
  • 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) – phil294 Jun 24 '21 at 09:58

16 Answers16

409

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.

Jacob Rask
  • 22,804
  • 8
  • 38
  • 36
  • 2
    Also see [Error "ResizeObserver loop limit exceeded" Firing When Video Controls Exist](https://crbug.com/809574). – Mark G Feb 20 '20 at 22:53
  • 3
    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 `preventDefault` – JohnnyFun Jul 10 '20 at 13:09
  • @JohnnyFun any thoughts on how to do that on safari? No matter what I only get "script error." when this event happens – NSjonas Aug 21 '20 at 19:49
  • 2
    I'm not sure it's always sensible/safe to ignore this error. It can indicate a performance problem, for example: https://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. – Oliver Joseph Ash Jul 28 '21 at 13:44
  • Also, there's a good description of why this error happens here: https://webkit.org/blog/9997/resizeobserver-in-webkit/#:~:text=observation.-,How%20Observations%20are%20Delivered,ResizeObserver,-strictly. (In Safari the error message is slightly different but the logic still applies.) – Oliver Joseph Ash Jul 29 '21 at 08:59
  • 6
    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: https://github.com/OliverJAsh/resize-observer-loop-tests – Oliver Joseph Ash Jul 29 '21 at 14:31
  • Related ticket, including the error message for Firefox and a little workaround: https://stackoverflow.com/a/64257593/472804 – retrovertigo Oct 14 '21 at 04:14
  • @JohnnyFun 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. – Ashish Aug 01 '23 at 04:21
  • 1
    Something like this: `window.addEventListener('unhandledrejection', e => { if (/resizeobserver/.test(e?.message || e || '').toString()) e.preventDefault(); e.stopImmediatePropagation() })` – JohnnyFun Aug 01 '23 at 05:03
98

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
   });
});
Flynn Hou
  • 429
  • 1
  • 6
  • 18
Rani
  • 6,424
  • 1
  • 23
  • 31
  • 6
    is the condition needed? "!Array.isArray(entries) || !entries.length" – Saeed Seyfi Jul 29 '20 at 13:14
  • What do you mean? – Rani Jul 30 '20 at 16:26
  • 5
    How did you know to do this...? – ADJenks Sep 30 '20 at 17:01
  • 2
    @ADJenks 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 frame – bzk Mar 20 '21 at 18:48
  • 15
    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: https://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. – Vaughan Hilts Apr 02 '21 at 03:28
  • 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. – Kostiantyn Ko Sep 30 '21 at 23:41
  • Note that this trick means your resizes will always be lagging one frame behind, which may not always be what you want. – trusktr Nov 08 '22 at 23:45
60

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.

Khateeb321
  • 1,861
  • 23
  • 25
  • 5
    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. – Wouter van Vliet Mar 22 '22 at 08:55
  • 4
    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. – Armen Michaeli May 09 '22 at 10:35
  • 2
    I can confirm that `if (err.message.includes("ResizeObserver loop limit exceeded"))` works as well as the regex. – Noumenon Mar 21 '23 at 02:50
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.

jross
  • 259
  • 3
  • 5
  • 3
    Or just check in incognito mode. Most people are likely to have either none, or far fewer extensions enabled in incognito. – Jayant Bhawal Apr 03 '20 at 19:00
  • +1 worked for me. Im my case, it was "Avira" chrome extension causing the error "ResizeObserver loop limit exceeded" – faza Apr 26 '21 at 06:41
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..

Jan Kretschmer
  • 119
  • 1
  • 2
  • 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](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror#window.onerror)). – Harry Cutts Nov 03 '21 at 00:49
  • 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. – Armen Michaeli May 09 '22 at 10:37
  • Where is `arguments` coming from? – JoeTidee Apr 18 '23 at 21:45
  • @JoeTidee https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments – Damien Golding Jul 11 '23 at 13:55
7

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.

vasekch
  • 1,207
  • 1
  • 10
  • 9
  • 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 https://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. – Vlad Moyseenko Jun 14 '22 at 12:07
6

add debounce like

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

fixed this error for me

Kreja
  • 271
  • 3
  • 14
  • 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. – chantey Feb 10 '22 at 00:39
3

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

Cypress.on(
  'uncaught:exception',
  (err) => !err.message.includes('ResizeObserver loop limit exceeded')
);
Liko
  • 2,130
  • 19
  • 20
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]);

dominikj111
  • 348
  • 5
  • 15
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;
JoG
  • 962
  • 2
  • 11
  • 17
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.

itayad
  • 289
  • 2
  • 10
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');
        }
      }
    })
  }, [])
Alex Song
  • 11
  • 2
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;
  };
}, []);
DanB-Web
  • 33
  • 1
  • 6
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"
  ]
}
perorororo
  • 191
  • 1
  • 14
0

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.

diachedelic
  • 2,195
  • 1
  • 24
  • 28
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');
Noumenon
  • 5,099
  • 4
  • 53
  • 73