{"record":{"id":"7ed5ccb1703bead4","repo":"emberjs/ember.js","slug":"you-can-t-call-rerender-on-a-view-being-destroyed","errorCode":null,"errorMessage":"You can't call rerender on a view being destroyed","messagePattern":"You can't call rerender on a view being destroyed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@ember/-internals/views/lib/views/states.ts","lineNumber":105,"sourceCode":"        set(value) {\n          if (value !== elementId) {\n            throw new Error(\"Changing a view's elementId after creation is not allowed\");\n          }\n        },\n      });\n    }\n  },\n});\n\nconst DESTROYING: Readonly<ViewState> = Object.freeze({\n  ...DEFAULT,\n\n  appendChild() {\n    throw new Error(\"You can't call appendChild on a view being destroyed\");\n  },\n\n  rerender() {\n    throw new Error(\"You can't call rerender on a view being destroyed\");\n  },\n});\n\n/*\n  Describe how the specified actions should behave in the various\n  states that a view can exist in. Possible states:\n\n  * preRender: when a view is first instantiated, and after its\n    element was destroyed, it is in the preRender state\n  * hasElement: the DOM representation of the view is created,\n    and is ready to be inserted\n  * inDOM: once a view has been inserted into the DOM it is in\n    the inDOM state. A view spends the vast majority of its\n    existence in this state.\n  * destroyed: once a view has been destroyed (using the destroy\n    method), it is in this state. No further actions can be invoked\n    on a destroyed view.\n*/","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/emberjs/ember.js/blob/26f97246a8bf2e28edf26ac3093da2e86c04ffc5/packages/@ember/-internals/views/lib/views/states.ts#L87-L123","documentation":"Ember view lifecycle states enforce which operations are legal on a view. When a view has entered the 'destroying' state, calling rerender() is forbidden because the view's renderer and DOM context are being torn down. The states machine in states.ts throws this Error to prevent re-render work on a dead view.","triggerScenarios":"Calling view.rerender() after the view has begun destruction — e.g. during willDestroyElement, willDestroy, or from a component triggered asynchronously (timer, promise) that resolves after the component is being destroyed.","commonSituations":"Async callbacks (setTimeout, debounce, promise resolution, event listeners) firing while a component unmounts during route transition; tests that tear down the app while rerender is queued.","solutions":["Guard rerender calls with an isDestroying/isDestroyed check","Cancel async work in willDestroy (cancel timers, resolve/abort promises)","Use @ember/runloop schedule/cancel so pending work is cancelled on teardown","Remove event listeners and observers on destroy"],"exampleFix":"// before\ndidReceiveAttrs() {\n  fetch('/data').then(() => this.rerender());\n}\n// after\ndidReceiveAttrs() {\n  fetch('/data').then(() => {\n    if (!this.isDestroying && !this.isDestroyed) this.rerender();\n  });\n}","handlingStrategy":"validation","validationCode":"if (!view.isDestroying && !view.isDestroyed) { view.rerender(); }","typeGuard":"function canRerender(view) { return view && !view.isDestroying && !view.isDestroyed; }","tryCatchPattern":"try { view.rerender(); } catch (e) { if (!/being destroyed/.test(e.message)) throw e; /* ignore teardown race */ }","preventionTips":["Check isDestroying/isDestroyed before any late async DOM work","Cancel runloop timers and async tasks in willDestroy","Use lifecycle-safe patterns ({{modify}} / decorators) that no-op after teardown","Avoid raw setTimeout for UI updates"],"tags":["ember","view-lifecycle","component-destruction","async"],"backgroundTag":"rerender-on-destroyed-view","analyzedSha":"26f97246a8bf2e28edf26ac3093da2e86c04ffc5","analyzedAt":"2026-09-01T05:01:28.182Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}