{"record":{"id":"fec7c69134d5a400","repo":"emberjs/ember.js","slug":"infinite-rendering-invalidation-detected","errorCode":null,"errorMessage":"infinite rendering invalidation detected","messagePattern":"infinite rendering invalidation detected","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@ember/-internals/glimmer/lib/base-renderer.ts","lineNumber":208,"sourceCode":"\nfunction resolveRenderPromise() {\n  if (renderSettledDeferred !== null) {\n    let resolve = renderSettledDeferred.resolve;\n    renderSettledDeferred = null;\n\n    _backburner.join(null, resolve);\n  }\n}\n\nlet loops = 0;\nfunction loopEnd() {\n  for (let renderer of renderers) {\n    if (!renderer.isValid()) {\n      if (loops > ENV._RERENDER_LOOP_LIMIT) {\n        loops = 0;\n        // TODO: do something better\n        renderer.destroy();\n        throw new Error('infinite rendering invalidation detected');\n      }\n      loops++;\n      return _backburner.join(null, NO_OP);\n    }\n  }\n  loops = 0;\n  resolveRenderPromise();\n}\n\n_backburner.on('begin', loopBegin);\n_backburner.on('end', loopEnd);\n\ntype Resolver = ClassicResolver;\n\ninterface RendererData {\n  owner: object;\n  context: EvaluationContext;\n  builder: IBuilder;","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/emberjs/ember.js/blob/26f97246a8bf2e28edf26ac3093da2e86c04ffc5/packages/@ember/-internals/glimmer/lib/base-renderer.ts#L190-L226","documentation":"During a Backburner loop-end flush, Ember re-validates renderers; if a renderer remains invalid after the rerender loop limit (ENV._RERENDER_LOOP_LIMIT), invalidation never converges, so Ember destroys the renderer and throws. This guards against endless revalidation (typically CP churn).","triggerScenarios":"Computed properties or observers whose getters invalidate themselves or each other; a template depending on a value that changes every time it's read; re-render loop exceeding the limit.","commonSituations":"A getter returning a new array/object each access feeding a tracked dependency; mutually dependent computeds; mutating state during render; ember-metal observer cycles after upgrades.","solutions":["Find the self-invalidating computed/tracked property (log reads/writes) and make it stable (cache the array/object)","Avoid mutating observed/tracked state inside getters or during render","Break mutual dependencies between computeds","Temporarily raise ENV._RERENDER_LOOP_LIMIT only to diagnose, not to fix"],"exampleFix":"// before\nget items() {\n  return this.model.filter(x => x.active).map(x => ({ ...x })); // new array every access\n}\n// after\nget items() {\n  return this.model.filter(x => x.active); // or memoize tracked state\n}","handlingStrategy":"validation","validationCode":"// detect self-invalidating computeds in dev\nget foo() { assert('no writes in getters', !this._writing); }","typeGuard":null,"tryCatchPattern":"try { renderApp(); } catch (e) { if (e.message === 'infinite rendering invalidation detected') { /* inspect recently-read tracked/computed props */ } throw e; }","preventionTips":["Never mutate tracked/observed state in getters or during render","Return stable objects/arrays from computeds","Avoid circular computed dependencies","Run dev-mode observer/track validation regularly"],"tags":["ember","rendering","infinite-loop","computed-property"],"backgroundTag":"rerender-loop-limit","analyzedSha":"26f97246a8bf2e28edf26ac3093da2e86c04ffc5","analyzedAt":"2026-09-01T05:01:28.182Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}