Definitive list of possible reasons NSManagedObjectContext save might fail - ios

TL;DR version: is there a definitive list of possible reasons that -[NSManagedObjectContext save:] could fail? I'm looking in CoreDataErrors.h, but it's not clear which of the keys relate to NSDetailedErrorsKey.
Background: I'm maintaining a shipping app written in a combination of Swift and Objective-C. It uses JSQCoreDataKit. Someone else wrote the code originally.
We have a production crash that we have not been able to reproduce. We can tell from the stacktrace that it is crashing in an error handler following an NSManagedObjectContext save failure with EXC_BAD_ACCESS trying to log the NSError to the console. The save failure is on a child context and we know the model class which was being modified at the time.
Hence we can tell that the save is failing, but we have no detailed information about the reason for the save failure.
We want to work backwards from the reason for the save failure, in the hopes of working out a reproduction for the crash, in order to test any potential fixes.
The crash is actually a side effect of the save failure. So although we want to fix the crash, we also need to work out the reason for the save failure.
For 95% of users, the save happens without problems.
The model has some non-optional fields and the data comes from JSON parsed from a server response. At this stage we have no reason to suspect the server is sending us bad data.
Does anyone know of a definitive list of possible reasons for a save failure that we could work through, eliminating options?
So far, I'm aware of:
validation failure e.g. a missing a required value, or a value outside specified max/min values in the model.
a possible save conflict (see NSPersistentStoreSaveConflictsErrorKey). But it's not clear if it's possible to have this when saving from a child context to a parent context.

There should never be a failure -save: with no error. Every time I have seen that situation it was my fault in my code somewhere.
Can you update the question with showing the code around the error location?
The possible failures of a save are:
Merge failure (most common)
Validation failure (less common)
No store defined in the NSPersistentStore
A nil NSManagedObjectContext (this would present the case you describe)
All of those except for the last will produce an error object to interrogate.

Related

'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for

I'm seeing some bizarre behaviour in a test. This error appears only on the second test run and after. At the end of each test, I delete the Core Data objects I've created during the test. If I comment out the cleanup, and don't delete the test objects, I don't seem to get the error.
This sort of matches up with the descriptions of this error I've read elsewhere. What's weird is this doesn't seem particularly related to threading (it occurs across test-runs). I've gone so far as to look at the sqlite tables my test leaves behind. The test leaves the database in largely the same state it found it in, so it's really hard for me to see what this error is upset about. What other state does Core Data keep in this regard, and how can I examine it?
If you have any ideas, please let me know.

EXC_BAD_ACCESS on mergeChangesFromContextDidSaveNotification

We have been trying to debug a Core Data multiple-context/threading issue wherein merging a Core Data save notification into our main thread NSManagedObjectContext is sporadically crashing the app. This is crashing ~2% of our app sessions and we are at a loss as to how to solve this. We would really appreciate any guidance or general advice on what could possibly cause this crash.
We have a Core Data setup that looks like this:
N.B. This is the default Core Data stack in Magical Record v2.3 created from [MagicalRecord setupAutoMigratingCoreDataStack]
This is the scenario where our app is crashing:
HTTP request returns JSON
JSON is parsed into NSManagedObjects (Some new entities, some updated entities) on Root Saving Context
Root Saving Context saves to persistent store
NSManagedObjectContextDidSaveNotification is broadcast by Core Data. Default context on main queue observes this and calls mergeChangesFromContextDidSaveNotification: with the NSDictionary of changes on the main thread.
It crashes when objectID is sent to an invalid object (most likely NSManagedObject has been deallocated).
This is occurring inside the private implementation of NSManagedObjectContext mergeChangesFromContextDidSaveNotification: so it is impossible for us to see what has actually gone wrong here; all we can tell at this point that an object which should exist, does not.
This only happens on a small percent of Core Data saves, indicating that may not be a fundamental flaw in our Core Data → API stack. Moreover, there is no indication that the size or type of the changes (insertions/updates/deletions) in the context changes have any impact on the likelihood of the crash.
The documentation of NSManagedObjectContextDidSaveNotification says that:
"You can pass the notification object to mergeChangesFromContextDidSaveNotification: on another thread, however you must not use the managed object in the user info dictionary directly on another thread. For more details, see Concurrency with Core Data in Core Data Programming Guide."
Maybe this is the issue? I would make sure the object u get from the notification is getting saved on Default Context on the same thread it was posted by Root.
It's been some time now since this question was posted and after rediscovering it I'd like to answer my own question for the sake of others who find this thread.
In my circumstance I had migrated a large code base from sibling NSManagedObjectContexts updated via NSManagedObjectContextDidSaveNotification's. However the problem was not really anything to do with this, even though this did expose the issue.
The real cause of this were places that there were older parts of the code, setup by previous engineers, that had setup KVO on NSManagedObjects and their properties. It transpired that KVO on Core Data entities is in fact a very very bad idea.
More accurately, it appeared that this happened when KVO was setup on entities and either the object, or the target of a relationship on this object was deleted from the NSPersistentStore. This second condition seemed to not be the only cause of the issue, but was definitely a very prominent cause in my situation.
Lesson's learnt:
Use a fetched results controller when you need to. KVO is not a convenient shortcut and you shouldn't avoid migrating dodgy Core Data KVO code to NSFetchedResultsControllers or another sensible alternative as the procrastination will just hurt you.
Multi threaded Core Data is a difficult but very worthwhile skill to become an expert in. Knowing your Core Data stack and the nuances and limitations of Core Data multithreading is absolutely worth all the mental anguish.
One possibility is that your persistent store has become corrupted and is in an inconsistent state. If this happens an error code is generated which Magical Record does not necessarily deal with. This can be the source of a number of difficult-to-repeat apparently-random crashes related to Magical Record (and may or may not be considered a Magical Record bug).
It's worth reading the Magical Record issues threads here (same issue) and here (different issue, but could be similar cause). When I hit these problems I managed to make some temporary patch fixes following various hints in those threads, but ultimately I decided to remove my dependency on Magical Record, and I have had no problems since then.

Best Way To Fix Core Data Migration Error

I have an application which uses core data and I have set up lightweight migration. When you first create the app xcode generates default methods. The persistentStoreCoordinator getter calls the abort() method and it says it should be replaced in a shipping application. I think the best way for my app to handle this failed migration would be to completely reset core data and then reload all the info. Is this the best method and if so how would I go about doing this?
The "best" answer always depends on your application.
This error should only ever happen during development. With proper testing, you should never see this error in production. I always consider it a development level error and therefore a NSLog and abort() are appropriate.
If your data is replaceable (i.e. you are using CD to cache data from the internet, etc.) then yes it makes sense to delete the database on this error and rebuild it from scratch. If your data is not replaceable then this is a terrible solution, see point 1.
Update 1
Putting this in an update because it is important and I don't want it getting lost in the comments:
#MarcusS.Zarra I know that this error should never happen, however I don't think it is unrecoverable.
In production, if you error on -addPersistentStore... it is unrecoverable as far as that database otherwise you would not have hit the error in the first place. Hence the two suggestions above, test more or replace the data.
There are two errors (ignoring iCloud) that you will get from that method, failed migration or failed to open the file.
If you failed to open the file the file is dead and is not going to be opened without human intervention.
If you failed the migration then you are missing the source data model and you are not going to recover it without a new build of the application that includes the source model.
Having a bad data file is extraordinarily rare. It indicates a fault with the file system or some other extremely unusual circumstance.
Not having the source data model is a 100% developer avoidable situation.
In either case, in production, you are not recovering from the error.
For development purposes I've previously added code to delete the database file before abort() is called.. That way when you relaunch the app, the database is reconfigured automatically to save you having to manually delete the app

How to make NSManagedObject not fault?

I'm currently debugging a big project written by another developer. The project uses CoreData which i am very new to. I'm having a crash which happens due to the fact that some NSManagedObject is being a fault(i have poor understanding of what fault is) and i would like to convert the object into "not fault" and see if it helps. Reading documentation made me think that to make object not fault is equal to to fire fault (again i have poor understanding of what is "to fire"), so i decided to fire it by calling any method on it, and this method is hasChanges since it's not in the list of methods which don't fire fault. However even after calling this method the object still remained being fault. Can anyone give me an example of how to convert NSManagedObject into "not fault" state?
The exception you mentioned in a comment is:
Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x1f0627a0 <x-coredata://E40418A0-A8E5-4340-865F-A9DA2E0095DD/CoreObject/p288>''
The "could not fulfill a fault" message indicates that this is not simply a problem with firing the fault. In fact there are no special steps to fire a fault-- you just access the attributes, and if necessary, the fault fires automatically.
What this error is telling you is that you're doing something that causes a fault to fire, but that Core Data can't find any record of the instance you're using. This can happen in certain scenarios, for example:
Fetch an object, and leave it as a fault (i.e. don't access any attributes)
Delete it from Core Data, but keep a reference to the object (maybe in an instance variable)
Save changes
Try to access an attribute on the object you fetched in step 1.
At this point Core Data would normally use the object to look up the attribute value. But you already deleted it, so the attribute data is gone. Core Data throws this exception.
There are some other scenarios that can trigger this-- calling reset on a managed object context while keeping previously fetched objects around, or removing the persistent store (so that the data still exists in the store file, but the file isn't loaded anymore). In general it means Core Data is trying to look up data on an object that's no longer valid.
What you need to do:
Figure out which object is causing this. Setting an exception breakpoint, so that the debugger loads just as the crash is happening, is probably a good idea.
Get rid of that object. Preventing the fault from firing might prevent this specific crash, but as long as you still have this object, it's like a land mine in your app. It will explode and crash the app as soon as you touch it.
Figure out why you have invalid managed objects lurking in memory. Maybe you should have gotten rid of them earlier? Maybe you're accidentally deleting something you don't want to delete? For some reason you're keeping managed objects around when they're no longer valid. This is the core problem that's causing your trouble.
Faulting is the process whereby an object and/or its properties are fetched from the database on-demand.
For example, if you attempt to access a property or relation on some object, person.name, that object may not have its data in-memory, and it has to be fetched from the underlying data store. This is a fault.
Faulting is a normal part of how Core Data works, and should not be related to crashes. An object will be unfaulted when you try to access its properties and relations.

unexplained behavioural difference between objectWithID: versus existingObjectWithID:

I understand the documented differences between these two calls. However does anyone know the reasons for the following observed behaviour I have noticed:
If I have a parentContext and a temporary childContext where I use the childContext to edit, insert and deleted objects, if use [childContext objectWithID:objectID]; to retrieve a known existing managed object, present in the parent context, it will sometimes give me an object with a fault that on being fired fails and generates an exception. I understand objectWithID: will, by design, always return an object in a faulted state regardless of if an actual managedObject exists for the given objectID. However if the object actually exists in the parent context, I would expect that when any of the properties are accessed, the object will always be successfully retrieved from the parent context (e.g. the fault will be fired) without any problem. If I use [childContext existingObjectWithID:objectID]; I find it does indeed always succeed.
For the record, I have turned off caching on the child context and this same behaviour occurs after [childContext resetContext] has been called - so it's not an artefact of old cached data hanging around that is inconsistent with the parent context.
The documentation alone seems to me to be insufficient to explain this behaviour. I can of course chalk it up to experience and just say "I now know to always use existingObjectWithID: when passing object IDs to my child edit context perform block" but I feel uneasy and would like to understand exactly what is going on here (not least so I can understand if there is any performance impact of using the one as over the other but also to understand what the constraint is so I can ensure there isn't some bad practice I'm unnecessarily implementing in my code and then using a wrong or inefficient call to fix it).
I've seen this behavior in my own code (the stack traces were from Mars, took forever to track down the cause of the issue), and my solution was the same (move from use of objectWithID: in the child context to use of existingObjectWithID:).
In my case, I'd create an object on the parent context, immediately obtain a permanent object ID for it, and ask for a save using UIManagedDocument's updateChangeCount:UIDocumentChangeDone, which will schedule a save at some point in the future. I think this point is key for the discussion.
I'd then perform a network call to obtain XML data pertaining to the newly-created object, and parse and import that data into Core Data. This occurred on a background thread, and I'd pass the object ID to the thread, which used a thread-confined child import context.
Under iOS5, I'd use objectWithID: in the worker thread to fault the new object into the import context. Worked fine, no issues.
Under iOS6, this began to fail, with bizarre stack traces, and the only solution was to move to existingObjectWithID:. Under testing, it appears to be a solid solution with this change in place.
Like you, I've tried to find some definitive statement as to why this works, but I've been unsuccessful in doing so. However, I think the pattern that my code exhibited and the stack traces I was seeing perhaps explains what's happening. I was always seeing a crash attempting to obtain data from the persistent store. I believe that under iOS5, the save I requested via UIManagedDocument was occurring before my child thread got a chance to run, and thus the new object was persisted before I called objectWithID:. It would appear from my crash logs that that is not the case under iOS6; the thread runs before the object has made it to the persistent store, and thus it can't be fetched into the child context yet. My assumption is that existingObjectWithID: is ensuring that any pending SQL I/O is performed before attempting a fetch, and thus the persistent store is consistent when the fetch occurs in my worker thread. I have been unable to find anything definitive to support this, but extensive testing seems to support that that's what's happening.
Having the same issue as the OP, and while I think the exception is by design as per
If the object is not registered in the context, it may be fetched or returned as a fault. This method always returns an object. The data in the persistent store represented by objectID is assumed to exist—if it does not, the returned object throws an exception when you access any property (that is, when the fault is fired). The benefit of this behavior is that it allows you to create and use faults, then create the underlying data later or in a separate context.
in the Apple docs -- that is, the data is NOT in the persistent store yet, it's sitting in the parent context -- it still seems odd that I simply cannot get my realized object in the parent context. Why must the persistent store have to know about it, for my object to be populated?
Getting the "existing object" using existingObjectWithID before getting the "crash object" using objectWithID will lead to both objects being equal.
The other way round the app will crash as soon as a property of the "crash object" is accessed. In this case the objects are not equal. The "crash object" has a temporary ObjectID.

Resources