Debugging NSObjectInaccessibleException – The NSManagedObject with ID:0x123456789 has been invalidated

| | August 4, 2015

I have a bug I’m struggling to track down. I believe what’s happening is that I’m deleting an object from the underlying database whilst another managed object context (in another thread) has a fault on it and gets the ‘NSObjectInaccessibleException’ when it tries to fulfil the fault.

The scenario is that I have a view accessing the data through one context meanwhile in the background, another threat is purging out of date records from the store. The background thread should only be purging objects which are not required by the view – this obviously isn’t the case but I’m having trouble tracking down exactly what happens. By the time I see the defect, it’s too late and it is a relatively rare defect that mainly only happens in the field.

Hence my question: Are there any tricks I’m missing when debugging CoreData – can I track lifetimes of objects from one context in another? I.e. when I delete my object is there an easy way to see if any other contexts have a reference to that same object? Using that, I could build some test code to check my logic and find the error.

4 Responses to “Debugging NSObjectInaccessibleException – The NSManagedObject with ID:0x123456789 has been invalidated”

  1. I just faced this issue me too. I did some refactoring to follow Apple’s “find-or-create” pattern for a bulk import of data. I created a new context dedicated to the import, by setting the undomanager to nil as suggested.
    So:

    // create a new special context for the bulk import of data
    NSManagedObjectContext *importContext = [[NSManagedObjectContext alloc] init];
    [importContext setPersistentStoreCoordinator:_persistentStoreCoordinator];
    
    // avoid tracking for undo/redo operations
    [importContext setUndoManager:nil];
    

    then I created a fetchRequest to retrieve ids of objects stored in the database, and inside the import loop I tested object’s id to find if it was contained inside the array of ids retrieved… the problem was that at a given interval I was saving the importContext and resetting it. And since I did erroneously referring to importContext rather than defaultContext I get that error. I fixed simply by changing:

    NSArray *storedObjects = [importContext executeFetchRequest:checkRequest error:&fetchError];
    

    with:

    NSArray *storedObjects = [defaultContext executeFetchRequest:checkRequest error:&fetchError];
    
  2. The solution was a combination of cleanup and this mapkit bug. a map view was holding onto its delegate after I had released my NSManagedObjectContext. Mapkit asked the delegate for the co-ordinates of an annotation and my delegate object tried to query an object that was in a released context (similar to Jason’s problem).

    The fix was as described in Jake’s blog post – set the delegate to nil when you are finished with the map view.

  3. I encountered this error earlier and the culprit was that I was cleaning up (completed released) my context and then later tried to access an object (previously) managed by that context.

    In my case, the context was a “scratch” context that goes away when the view was closed. However, I had a background job that the view had spawned that wanted to update the object.

    I ended up making an accessor for the managed object that returned nil when [managedObject isFault] was true. Then in my code I was checking the value of that accessor selector to make sure I had a valid object to work with (say when my background job finally finished its work).

    I’m pretty new to Core Data, so there’s probably a better/smarter way to do this, but I think it fixed the issue for me.

  4. What is the second context doing when it tries to fault in the object which has been deleted from the persistent store?

    This sounds like a bug which may have 2 parts: you aren’t merging changes from your peer context, and you have a logic bug which is causing you to use an object in thread B which has been deleted in thread A.

    Typically you’ll want to merge changes from a peer context using -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:].

Leave a Reply