The following complex migration consistently crashes the app with the following error:
SwiftData/PersistentModel.swift:726: Fatal error: What kind of backing data is this? SwiftData._KKMDBackingData<SwiftDataMigration.ItemSchemaV1.ItemList>
My app relies on a complex migration that involves these optional 1 to n relationships. Theoretically I could not assign the relationships in the willMigrate block but afterwards I am not able to tell which list and items belonged together.
Steps to reproduce:
- Run project
 - Change typealias CurrentSchema to ItemSchemaV2 instead of ItemSchemaV1.
 - Run project again -> App crashes
 
My setup:
- Xcode Version 16.2 (16C5032a)
 - MacOS Sequoia 15.4
 - iPhone 12 with 18.3.2 (22D82)
 
Am I doing something wrong or did I stumble upon a bug? I have a demo Xcode project ready but I could not upload it here so I put the code below.
Thanks for your help
typealias CurrentSchema = ItemSchemaV1
typealias ItemList = CurrentSchema.ItemList
typealias Item = CurrentSchema.Item
@main
struct SwiftDataMigrationApp: App {
    var sharedModelContainer: ModelContainer = {
        do {
            return try ModelContainer(for: ItemList.self, migrationPlan: MigrationPlan.self)
        } catch {
            fatalError("Could not create ModelContainer: \(error)")
        }
    }()
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(sharedModelContainer)
    }
}
This is the migration plan
enum MigrationPlan: SchemaMigrationPlan {
    static var schemas: [any VersionedSchema.Type] {
        [ItemSchemaV1.self, ItemSchemaV2.self]
    }
    
    static var stages: [MigrationStage] = [
        MigrationStage.custom(fromVersion: ItemSchemaV1.self, toVersion: ItemSchemaV2.self, willMigrate: { context in
            print("Started migration")
            let oldlistItems = try context.fetch(FetchDescriptor<ItemSchemaV1.ItemList>())
            for list in oldlistItems {
                let items = list.items.map { ItemSchemaV2.Item(timestamp: $0.timestamp)}
                let newList = ItemSchemaV2.ItemList(items: items, name: list.name, note: "This is a new property")
                context.insert(newList)
                context.delete(list)
            }
            try context.save() // Crash indicated here
            print("Finished willMigrate")
        }, didMigrate: { context in
            print("Did migrate successfully")
        })
    ]
}
The versioned schemas
enum ItemSchemaV1: VersionedSchema {
    static var versionIdentifier = Schema.Version(1, 0, 0)
    static var models: [any PersistentModel.Type] {
        [Item.self]
    }
    @Model
    final class Item {
        var timestamp: Date
        var list: ItemSchemaV1.ItemList?
        init(timestamp: Date) {
            self.timestamp = timestamp
        }
    }
    
    @Model
    final class ItemList {
        @Relationship(deleteRule: .cascade, inverse: \ItemSchemaV1.Item.list)
        var items: [Item]
        var name: String
        init(items: [Item], name: String) {
            self.items = items
            self.name = name
        }
    }
}
enum ItemSchemaV2: VersionedSchema {
    static var versionIdentifier = Schema.Version(2, 0, 0)
    static var models: [any PersistentModel.Type] {
        [Item.self]
    }
    @Model
    final class Item {
        var timestamp: Date
        var list: ItemSchemaV2.ItemList?
        init(timestamp: Date) {
            self.timestamp = timestamp
        }
    }
    
    @Model
    final class ItemList {
        @Relationship(deleteRule: .cascade, inverse: \ItemSchemaV2.Item.list)
        var items: [Item]
        var name: String
        var note: String
        init(items: [Item], name: String, note: String = "") {
            self.items = items
            self.name = name
            self.note = note
        }
    }
}
Last the ContentView:
struct ContentView: View {
    @Query private var itemLists: [ItemList]
    var body: some View {
        NavigationSplitView {
            List {
                ForEach(itemLists) { list in
                    NavigationLink {
                        List(list.items) { item in
                            Text(item.timestamp.formatted(date: .abbreviated, time: .complete))
                        }
                        .navigationTitle(list.name)
                    } label: {
                        Text(list.name)
                    }
                }
            }
            .navigationTitle("Crashing migration demo")
            .onAppear {
                if itemLists.isEmpty {
                    for index in 0..<10 {
                        let items = [Item(timestamp: Date.now)]
                        let listItem = ItemList(items: items, name: "List No. \(index)")
                        modelContext.insert(listItem)
                        
                    }
                    try! modelContext.save()
                }
            }
        } detail: {
            Text("Select an item")
        }
    }
}