CoreData error=134100 Failed to open the store

Hello,

I'm using CoreData + CloudKit and I am facing the following error 134100 "The managed object model version used to open the persistent store is incompatible with the one that was used to create the persistent store."

All my schema updates are composed of adding optional attributes to existing entities, adding non-optional attributes (with default value) to existing entities or adding new entities Basically, only things that lightweight migrations can handle.

Every time I update the schema, I add a new model version of xcdatamodel - who only has a single configuration (the "Default" one). And I also deploy the updated CloudKit schema from the dashboard.

It worked up to v3 of my xcdatamodel, but started to crash for a few users at v4 when I added 16 new attributes (in total) to 4 existing entities.

Then again at v5 when I added 2 new optional attributes to 1 existing entity.

I'm using a singleton and here is the code:


private func generateCloudKitContainer() -> NSPersistentCloudKitContainer {
    let container = NSPersistentCloudKitContainer(name: "MyAppModel")
    let fileLocation = URL(...)
    let description = NSPersistentStoreDescription(url: fileLocation)
    description.shouldMigrateStoreAutomatically = true
    description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
    description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
    let options = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.com.company.MyApp")
    options.databaseScope = .private
    description.cloudKitContainerOptions = options
    container.persistentStoreDescriptions = [description]
    container.viewContext.automaticallyMergesChangesFromParent = true
    container.loadPersistentStores { description, error in
        container.viewContext.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType)
        if let error {
            // Error happens here!
        }
    }
    return container
}

I can't reproduce it yet. I don't really understand what could lead to this error.

I would also like to add that I believe that for users facing this error, a simple restart of the app fixes the problem. I can't confirm that it is 100% the case for all of them, but it seems like it.

I had a report where a user who would use the app daily (always up to date) opened the app and suddenly got this error, killed the app, and it went away at the next app opening.

The message ("The managed object model version used to open the persistent store is incompatible with the one that was used to create the persistent store.") feels like a moot reason. If it was really incompatible, it shouldn't be able to auto-fix itself at next app launch.

I also believe that this thread shares the same root issue although we have different schema updates: https://developer.apple.com/forums/thread/748543 They added Fetch Index Elements, I added parameters.

If a CoreData team member was able to shred some light... Or at least give a direction.

This error 134100 is triggered when the following two conditions are both true:

  • The version hashes of your current model version and the existing store are incompatible.

  • In store options dictionary, NSMigratePersistentStoresAutomaticallyOption is set to false.

(The other reason is that the version of the Core Data store hash version is not compatible with the Core Data framework in the current system. Unless the configuration goes really crazy, I don't think this can happen.)

You have been very clear that your change on the Core Data model is lightweight migration compatible and that shouldMigrateStoreAutomatically is true, but I am wondering if the following cases can happen:

  1. You have the other code path, which is not necessarily in the same process, that opens the store with shouldMigrateStoreAutomatically being set to false.

  2. Your store is shared between multiple processes (your main app and an extension, for example) and the processes open the store with different model versions.

If you share a store between your main app and an extension, the app and extension can open and migrate the store simultaneously (because you don't control the life cycle of the extension), which may trigger a race and lead to the second case. In this situation, the crash won't happen again if the store has been migrated (before the crash) by one of the processes.

To be clear, what I said so far is purely a guess. To really get to the bottom of the issue, you might still need to have a reproducible case, which I understand is pretty hard.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Thank you very much for your answer Ziqiao!

I have a better understanding of the logic behind the error.

And yes I do have widgets! So this seems to be the best lead. I have added additional logging, hoping to confirm this theory. But it will take time and effort to be 100% sure.

Meanwhile, let's say this is really the culprit, how do you officially approach this issue?

I can think of these 2 potential solutions:

  1. Is there a way to "detect and wait" for a migration to be done? So that when the widget (or the main app) initializes the shared CoreData stack, I first check if a migration is ongoing and retry to open later?

  2. Do you recommend avoiding sharing the same CoreData stack between the main app and the widget? So I should create a separate CoreData file exclusive to widgets?

I can even think of a third solution which is close to manually doing solution 1. I create my own set of flags lastMigrationVersion and lastMigrationDate.

If I see that a stack (let's say the main app one) did trigger a lightweight migration, I can check if enough time was given for the migration before attempting to open the stack from elsewhere (let's say from the widget). But it means hardcoding a static delay of... let's imagine 1.5 second before opening a potentially migrating CoreData stack. I have no idea how long a migration can last unfortunately but I take that 1 second should be enough in the majority of cases? It's not a stable solution...

What do you recommend?

...let's say this is really the culprit, how do you officially approach this issue?

In that case, I'd consider having the app in charge of the the migration. Concretely:

  1. Before loading the store, your widget detects if the store is compatible with the current model version.

  2. If the store is not compatible, remind the user to launch the main app to do the migration. Otherwise, load the store as needed.

The code to detect the store compatibility looks like below:

var isVersionCompatibile = true

let container = NSPersistentContainer(name: "MyDetectModelCompatibility")
let storeURL = NSPersistentContainer.defaultDirectoryURL().appendingPathComponent("test.sqlite")
let storeDescription = NSPersistentStoreDescription(url: storeURL)

// Turn off lightweight migration options;
// Core Data will then return error if the model version is incompatibile.
//
storeDescription.shouldMigrateStoreAutomatically = false
storeDescription.shouldInferMappingModelAutomatically = false

container.persistentStoreDescriptions = [storeDescription]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
    if let error = error as NSError? {
        if error.code == NSPersistentStoreIncompatibleVersionHashError {
            isVersionCompatibile = false
        } else {
            // Handle other errors here.
        }
    }
})
// isVersionCompatibile now valid.

Since you are using Core Data + CloudKit, you'd consider having the app in charge of the the synchronization as well. I discussed this topic in the following technote:

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

CoreData error=134100 Failed to open the store
 
 
Q