Introduction

Core Data migrations keep the on-database store in sync with your managed object model. When a migration fails, the NSPersistentContainer cannot load the persistent store, and the app either crashes or operates without data. Lightweight migrations handle simple changes automatically, but complex schema changes require explicit mapping models or manual migration strategies.

Symptoms

  • NSCocoaErrorDomain Code=134130 - migration failed
  • Can't merge models with two different entities named
  • Persistent store migration failed on app launch after update
  • App works on fresh install but crashes for existing users
  • Console: NSManagedObjectModel: warning: Failed to load model versions

Example error: `` Error Domain=NSCocoaErrorDomain Code=134130 "The operation couldn't be completed. (Cocoa error 134130.)" UserInfo={ URL=file:///.../MyApp.sqlite, metadata={ NSPersistenceFrameworkVersion = 1234; NSStoreModelVersionHashes = { User = <abc123...>; }; }, reason=The model used to open the store is incompatible with the one used to create the store }

Common Causes

  • Migration options not passed to persistent store coordinator
  • Model changes are not compatible with lightweight migration
  • Missing or incorrect model version in the .xcdatamodeld
  • Renamed properties without renaming identifiers
  • Store created with a development model that differs from production

Step-by-Step Fix

  1. 1.Enable automatic lightweight migration:
  2. 2.```swift
  3. 3.lazy var persistentContainer: NSPersistentContainer = {
  4. 4.let container = NSPersistentContainer(name: "MyAppModel")

container.loadPersistentStores { description, error in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }

// Configure migration options BEFORE loading stores let description = container.persistentStoreDescriptions.first! description.setOption(true as NSNumber, forKey: NSMigratePersistentStoresAutomaticallyOption) description.setOption(true as NSNumber, forKey: NSInferMappingModelAutomaticallyOption)

return container }() ```

  1. 1.Create a new model version for schema changes:
  2. 2.`
  3. 3.Select your .xcdatamodeld file in Xcode
  4. 4.Editor > Add Model Version...
  5. 5.Name it "MyAppModel 2"
  6. 6.Select the new version and make your changes
  7. 7.Select the .xcdatamodeld file
  8. 8.File Inspector > Model Version > Set "MyAppModel 2" as Current
  9. 9.`
  10. 10.Use lightweight-migration-compatible changes:
  11. 11.```swift
  12. 12.// These changes support lightweight migration:
  13. 13.// - Adding a new attribute (with default value or optional)
  14. 14.// - Removing an attribute
  15. 15.// - Adding a new relationship (optional)
  16. 16.// - Making a required attribute optional
  17. 17.// - Renaming with renaming identifiers

// To set a renaming identifier: // 1. Select the old attribute in the old model version // 2. Data Model Inspector > Renaming ID // 3. Set it to the new attribute name ```

  1. 1.Handle migration failure gracefully:
  2. 2.```swift
  3. 3.container.loadPersistentStores { description, error in
  4. 4.if let error = error {
  5. 5.if (error as NSError).code == 134130 {
  6. 6.// Migration failed - delete and recreate store
  7. 7.self.recreatePersistentStore(container: container)
  8. 8.} else {
  9. 9.fatalError("Unresolved error \(error)")
  10. 10.}
  11. 11.}
  12. 12.}

func recreatePersistentStore(container: NSPersistentContainer) { let storeURL = container.persistentStoreDescriptions.first!.url! let coordinator = NSPersistentStoreCoordinator( managedObjectModel: container.managedObjectModel )

// Delete the old store try? FileManager.default.removeItem(at: storeURL) try? FileManager.default.removeItem(atPath: storeURL.path + "-shm") try? FileManager.default.removeItem(atPath: storeURL.path + "-wal")

// Create a new one try? coordinator.addPersistentStore( ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: nil ) } ```

Prevention

  • Always create a new model version before making schema changes
  • Test migrations by installing the new version over the old version
  • Keep a copy of the production SQLite file for migration testing
  • Use NSMigratePersistentStoresAutomaticallyOption and NSInferMappingModelAutomaticallyOption
  • For complex migrations, create explicit mapping models in Xcode
  • Add migration tests that create a store with the old model and migrate to the new one