Data cycle within ember-changeset triggers invalidation-after-set assertion
See original GitHub issueWhile validating, a changeset can consume its own _errors property before writing to it. This addon marks _errors as tracked:
But if you read and then write a tracked property during rendering, Ember will throw the dreaded:
You attempted to update
_errorsonchangeset:[object Object], but it had already been used previously in the same computation.
assertion.
Here is a tiny app that crashes at boot: https://github.com/ef4/--ember-changeset-bug-repro/commit/90a2a00cfc109f0bda5cf9dd4793532e173cb0c0
Both the read and the write happen on this line:
this[ERRORS] = this._deleteKey(ERRORS, key) as Errors<any>;
First, _deleteKey’s implementation consumes this._errors, and then we try to immediately assign to it.
I suspect the same bug is also lurking in rollbackInvalid(), which does the same thing.
The fix here is to make sure _errors is always only an output to your computation, never an input. Recompute the whole _errors whenever it is needed without reference to the old value. And if you need caching, make caches that are not tracked.
Issue Analytics
- State:
- Created 2 years ago
- Comments:14

Top Related StackOverflow Question
Also keep in mind that write-then-read is totally fine, it’s only read-then-write that is a problem.
(To think about why it’s a problem, consider that reading a tracked property causes that tracked property to be an automatic dependency of the code you’re running. If your code then changes that property, it just invalidated itself.)
No, that’s really not different.
The state the user sets is purely an input to the changeset. You would only ever read from it, never write it. The resulting validation state is purely an output of the changeset. You would only ever write to it, never read from it.
Yes, please don’t ever do this unless you’re deliberately forcing a double render and know why you want a double render (measuring DOM is the typical reason).