Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

model.unloadRecord causes Uncaught TypeError: Cannot read property 'eachAttribute' of null #5343

Closed
alexmiddeleer opened this issue Jan 30, 2018 · 10 comments · Fixed by #5376

Comments

@alexmiddeleer
Copy link

alexmiddeleer commented Jan 30, 2018

at new Snapshot (-private.js:5069)
    at InternalModel.createSnapshot (-private.js:6894)
    at Class._flushPendingFetchForType (-private.js:10771)
    at cb (ember-metal.js:5546)
    at OrderedSet.forEach (ember-metal.js:5358)
    at MapWithDefault.forEach (ember-metal.js:5554)
    at Class.flushAllPendingFetches (-private.js:10687)
    at invoke (backburner.js:258)
    at Queue.flush (backburner.js:151)

This is after calling findRecord then unloadRecord on the result. We are upgrading to ember 2.18 and ember-data 2.18, and this appears to be a regression.

Twiddle reproduction:
https://ember-twiddle.com/c16533267ff13353b25498fac4d05ca3?fileTreeShown=false&numColumns=2&openFiles=controllers.application.js%2Ctemplates.application.hbs

Possibly a duplicate of #4963, but that was supposedly fixed a while ago.

The code that doesn't work is this:

this.get('store').findRecord('person', 1).then((person) => {
   person.unloadRecord();
});
@alexmiddeleer alexmiddeleer changed the title Uncaught TypeError: Cannot read property 'eachAttribute' of null model.unloadRecord causes Uncaught TypeError: Cannot read property 'eachAttribute' of null Jan 30, 2018
@jlami
Copy link
Contributor

jlami commented Feb 8, 2018

Unloading the record from the controller.model seems to not give this error.
It actually really needs to be just after the findRecord.
I also have this problem in 2.17.

In my case I found out that it is triggered due to a ManyRelationship.findRecords() when one of the items in the relationship is being dematerialized. Maybe store._scheduleFetch should detect internalModel._isDematerializing and don't do anything?

@medokin
Copy link

medokin commented Feb 27, 2018

Repoduced in ember-data 3.0.1

@olivierchatry
Copy link

olivierchatry commented Mar 5, 2018

I have the same problems. If I do that it works :

record.destroyRecord().then(
	() => store.unloadRecord(record)
)

I tried to trace the code, and apparently, unloadRecord somehow schedule a fetch before the object is destroyed. I think it is because the hasMany relationship is changed ( record I'm trying to unload has a belongsTo ).

@buschtoens
Copy link
Contributor

buschtoens commented Mar 5, 2018

I get the same error, when I try to load a relationship by ID, that 404s.

For instance:

{
  id: '1',
  type: 'post',
  relationships: {
    author: { id: '2', type: 'author' }
  }
}

Now, if I access the author relationship and therefore trigger Ember Data to load it and the request fails with a 404, I get the same error:

Uncaught TypeError: Cannot read property 'eachAttribute' of null
    at new Snapshot (vendor.js:181070)
    at InternalModel.createSnapshot (vendor.js:182969)
    at Class._flushPendingFetchForType (vendor.js:186533)
    at cb (vendor.js:45496)
    at OrderedSet.forEach (vendor.js:45309)
    at MapWithDefault.forEach (vendor.js:45504)
    at Class.flushAllPendingFetches (vendor.js:186450)
    at invoke (vendor.js:27457)
    at Queue.flush (vendor.js:27377)
    at DeferredActionQueues.flush (vendor.js:27530)

To make things worse, this appears to put Ember Data in an invalid state, that requires a page reload.

@ewwilson
Copy link

ewwilson commented Mar 7, 2018

I've had issues similar to this with all releases after 2.12.2 and therefore have not been able to upgrade ember-data past that release

@wagenet
Copy link
Member

wagenet commented Mar 10, 2018

I've got an app on 3.0 and can consistently reproduce this error. Would be happy to help debug if anyone wants to provide me with some direction.

@sly7-7
Copy link
Contributor

sly7-7 commented Mar 12, 2018

@wagenet There are two failing tests in #5353 and #5354, maybe a good starting point ?

hjdivad added a commit to hjdivad/data that referenced this issue Mar 13, 2018
When fetching records, cancel internal model destruction if it is
scheduled, ie if the model is dematerializing because it was unloaded.

It is not possible to construct snapshots for dematerializing internal
models.  Prior to this commit, this could happen when fetching a record
in the same runloop that it was unloaded.  A straightfoward way of
getting into this state was via

```
store.findRecord('book', 1).then(b => b.unloadRecord)
```

when the model is already cached in the store.  Under these conditions,
the fetch is scheduled, then the promise fulfills with the cached record
and is unloaded and *then* the scheduled fetch is flushed.

[fix emberjs#5343]
@hjdivad
Copy link
Member

hjdivad commented Mar 13, 2018

Thanks for the detailed report and twiddle reproduction! (seriously that helps a lot and is much appreciated)

I believe #5376 solves this (it essentially implements @jlami's suggestion). Please let me know if you're still able to reproduce this with those changes.

Note that #5353 is a different issue (see #5377 for fix)

hjdivad added a commit to hjdivad/data that referenced this issue Mar 13, 2018
Invalidates link promises when inverse records are unloaded.  Subsequent
fetches of a `hasMany` unloaded in this way will load from the link
again, rather than loading whatever ids were returned from the prior
link load.

[fix emberjs#5343]
@alexmiddeleer
Copy link
Author

alexmiddeleer commented Mar 14, 2018

@hjdivad Thanks for the PR - looks like the problem is fixed in our app using hjdivad/data#1ae51810efee171f260cabf4c725e4abcaf1dfb8
as compared to ember-data ~2.18.0 👏

hjdivad added a commit to hjdivad/data that referenced this issue Mar 14, 2018
When fetching records, cancel internal model destruction if it is
scheduled, ie if the model is dematerializing because it was unloaded.

It is not possible to construct snapshots for dematerializing internal
models.  Prior to this commit, this could happen when fetching a record
in the same runloop that it was unloaded.  A straightfoward way of
getting into this state was via

```
store.findRecord('book', 1).then(b => b.unloadRecord)
```

when the model is already cached in the store.  Under these conditions,
the fetch is scheduled, then the promise fulfills with the cached record
and is unloaded and *then* the scheduled fetch is flushed.

[fix emberjs#5343]
hjdivad added a commit to hjdivad/data that referenced this issue Mar 14, 2018
When fetching records, cancel internal model destruction if it is
scheduled, ie if the model is dematerializing because it was unloaded.

It is not possible to construct snapshots for dematerializing internal
models.  Prior to this commit, this could happen when fetching a record
in the same runloop that it was unloaded.  A straightfoward way of
getting into this state was via

```
store.findRecord('book', 1).then(b => b.unloadRecord)
```

when the model is already cached in the store.  Under these conditions,
the fetch is scheduled, then the promise fulfills with the cached record
and is unloaded and *then* the scheduled fetch is flushed.

[fix emberjs#5343]
bmac pushed a commit that referenced this issue Mar 15, 2018
When fetching records, cancel internal model destruction if it is
scheduled, ie if the model is dematerializing because it was unloaded.

It is not possible to construct snapshots for dematerializing internal
models.  Prior to this commit, this could happen when fetching a record
in the same runloop that it was unloaded.  A straightfoward way of
getting into this state was via

```
store.findRecord('book', 1).then(b => b.unloadRecord)
```

when the model is already cached in the store.  Under these conditions,
the fetch is scheduled, then the promise fulfills with the cached record
and is unloaded and *then* the scheduled fetch is flushed.

[fix #5343]

(cherry picked from commit 7444444)
bmac pushed a commit that referenced this issue Mar 19, 2018
When fetching records, cancel internal model destruction if it is
scheduled, ie if the model is dematerializing because it was unloaded.

It is not possible to construct snapshots for dematerializing internal
models.  Prior to this commit, this could happen when fetching a record
in the same runloop that it was unloaded.  A straightfoward way of
getting into this state was via

```
store.findRecord('book', 1).then(b => b.unloadRecord)
```

when the model is already cached in the store.  Under these conditions,
the fetch is scheduled, then the promise fulfills with the cached record
and is unloaded and *then* the scheduled fetch is flushed.

[fix #5343]

(cherry picked from commit 7444444)
@rafaelbnp
Copy link

I'm having the exact same issue as @buschtoens mentioned here.

Any idea how to work around it?

runspired pushed a commit that referenced this issue Sep 14, 2018
When fetching records, cancel internal model destruction if it is
scheduled, ie if the model is dematerializing because it was unloaded.

It is not possible to construct snapshots for dematerializing internal
models.  Prior to this commit, this could happen when fetching a record
in the same runloop that it was unloaded.  A straightfoward way of
getting into this state was via

```
store.findRecord('book', 1).then(b => b.unloadRecord)
```

when the model is already cached in the store.  Under these conditions,
the fetch is scheduled, then the promise fulfills with the cached record
and is unloaded and *then* the scheduled fetch is flushed.

[fix #5343]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants