SwiftData crash on fetch

I have a strange crash which I have problems understanding. It only happens on a few devices, after a ModelContainer migration, and it doesn't seem to crash on the migration itself.

The fetch is done in onAppear, and shouldn't necessarily result in a crash, as it is an optional try:

let request = FetchDescriptor<Rifle>()
let data = try? modelContext.fetch(request)
if let data, !data.isEmpty {
    rifle = data.first(where: { $0.uuid.uuidString == settings.selectedRifleId }) ?? data.first!
}

When I get logs from users, there seems to be an error in encoding?

Exception Type:  EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x000000018e8bfd78
Termination Reason: SIGNAL 5 Trace/BPT trap: 5
Terminating Process: exc handler [71687]

Triggered by Thread:  0

Thread 0 name:   Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libswiftCore.dylib            	       0x18e8bfd78 _assertionFailure(_:_:file:line:flags:) + 264
1   SwiftData                     	       0x24e18b480 0x24e14c000 + 259200
2   SwiftData                     	       0x24e193968 0x24e14c000 + 293224
3   SwiftData                     	       0x24e195a78 0x24e14c000 + 301688
4   libswiftCore.dylib            	       0x18e8e4084 _KeyedEncodingContainerBox.encodeNil<A>(forKey:) + 352
5   libswiftCore.dylib            	       0x18e8d79f0 KeyedEncodingContainer.encodeNil(forKey:) + 64
6   SwiftData                     	       0x24e19f09c 0x24e14c000 + 340124
7   SwiftData                     	       0x24e1a3dec 0x24e14c000 + 359916
8   libswiftCore.dylib            	       0x18ec10be8 dispatch thunk of Encodable.encode(to:) + 32
9   SwiftData                     	       0x24e1cd500 0x24e14c000 + 529664
10  SwiftData                     	       0x24e1cd0c8 0x24e14c000 + 528584
11  SwiftData                     	       0x24e1da960 0x24e14c000 + 584032
12  SwiftData                     	       0x24e1ee2ec 0x24e14c000 + 664300
13  SwiftData                     	       0x24e1d97d8 0x24e14c000 + 579544
14  SwiftData                     	       0x24e1eada0 0x24e14c000 + 650656
15  SwiftData                     	       0x24e1d989c 0x24e14c000 + 579740
16  SwiftData                     	       0x24e1eee78 0x24e14c000 + 667256
17  Impact                        	       0x1027403bc 0x10268c000 + 738236

I am facing the same issue as well. fetch crashes, but on the same FetchDescriptor fetchCount return correct result. To be more specific. I have a @ModelActor in which I am making some fetch and insert calls. This modelActor is initialized with modelContainer from the @Environment modelContext on .onAppear, and then calls to insert are made. After insertion is done, fetch call is made.

The @ModelActor is a singleton to maintain a single modelContext across the app. And I am also initializing the .modelContainer as a @State instead of a let.

The app works on iOS 17, but crashes just like OP mentioned on iOS 18.

I've checked the SwiftData forum posts but no solution, is there anything anyone from the Apple Dev Team can point to regarding this?

swiftData is very busted on iOS 18. It broke all of my preview with swiftData. Somehow persistentModelID no longer works, too

I have crashes with the same stack trace from some users as well, both from iOS 17 and 18

I'm still experiencing this crash in iOS18. What trips me out is the assertionFailure in a build which should have assertionFailures...

Apple DTS asked me for a link to the forums, so I'm adding what I know, and send them here. The crash is hard to reproduce, so this is what I know as of now:

  • My app is a SwiftUI-app, with SwiftData
  • I create the ModelContainer in the App-structure:
var modelContainer: ModelContainer = {
        let modelContainer: ModelContainer
        let schema = Schema(versionedSchema: SchemaV7.self)
        let config = ModelConfiguration(cloudKitDatabase: .none)
        do {
            modelContainer = try ModelContainer(
                for: schema,
                migrationPlan: MigrationPlan.self,
                configurations: config
            )
        } catch {
            Logger().error("Error creating model container: \(error.localizedDescription)")
            preconditionFailure("Failed to create model container")
        }
        return modelContainer
    }()
  • When I add a new Schema, the migration completes successfully, but for some users (not all), the app crashes. If I delete the app and install the same app but with no database, the app runs as normal.
  • Migrations are done as custom migration steps, to provide logging options, but the crash happens still if the step is lightweight migration

A crash can be triggered by many reasons, and so to provide meaningful comments, folks may need more specific information. Typically, a minimal project with detailed steps to reproduce the crash will really help. (I understand that it's hard to create a reproducible case for a random crash though.)

Concretely in @audungk83's case, the crash is related to data migration, and the last frame of the stack trace shows a _assertionFailure call. Based on that, I am guessing if there is any optional attribute getting involved. As an example, if the migration process migrates an optional attribute that has no value to a non-optional attribute, accessing the migrated attribute will trigger a crash of that kind.

@audungk83 If you can provide your existing and new schema versions, and also your code that drives the migration, I may be able to take a further look.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

The images below are displaying the only difference between the two schemas:

This is the migration plan:

enum ImpactMigrationPlan: SchemaMigrationPlan {
    static var schemas: [any VersionedSchema.Type] {
        [
            ImpactSchemaV1.self,
            ImpactSchemaV1_1.self,
            ImpactSchemaV2.self,
            ImpactSchemaV3.self,
            ImpactSchemaV4.self,
            ImpactSchemaV5.self,
            ImpactSchemaV6.self,
            ImpactSchemaV7.self,
            ImpactSchemaV8.self
        ]
    }
    
    static var stages: [MigrationStage] {
        [migrateV1toV1_1, migrateV1_1toV2, migrateV2toV3, migrateV3toV4, migrateV4toV5, migrateV5toV6, migrateV6toV7, migrateV7toV8]
    }

....

 static let migrateV7toV8 = MigrationStage.custom(
        fromVersion: ImpactSchemaV7.self,
        toVersion: ImpactSchemaV8.self,
        willMigrate: { context in
            Logger.application.info("Will migrate to stage 8")
        }, didMigrate: { context in
        Logger.application.info("Did migrate to stage 8")

        })

And this is how the model container is built in the app:

 var modelContainer: ModelContainer = {
        let modelContainer: ModelContainer
        let schema = Schema(versionedSchema: ImpactSchemaV8.self)
        let config = ModelConfiguration(cloudKitDatabase: .none)
        do {
            modelContainer = try ModelContainer(
                for: schema,
                migrationPlan: ImpactMigrationPlan.self,
                configurations: config
            )
        } catch {
            Logger().error("Error creating model container: \(error.localizedDescription)")
            preconditionFailure("Failed to create model container")
        }
        return modelContainer
    }()

The logs after a failed migration could maybe be relevant:

CoreData: annotation: Completed persistent history metadata tables update
CoreData: annotation: Updating metadata
CoreData: annotation: Finished updating metadata
CoreData: annotation: Committing formal transaction
CoreData: annotation: Finished committing formal transaction
CoreData: annotation: Checkpointing WAL journal
CoreData: annotation: Finished checkpointing WAL journal
CoreData: annotation: Successfully completed lightweight migration on connection
CoreData: annotation:     Migration step 0.0 'Total migration time (on connection)' took 0.02 seconds
CoreData: annotation: (migration) in-place migration completed successfully in 0.02 seconds
Did migrate to stage 8
CoreData: annotation: (migration)	 Automatic schema migration succeeded for store at 'file:///var/mobile/Containers/Data/Application/74E75823-0F85-4194-A84D-B180739009BB/Library/Application%20Support/default.store' with migration stage: <NSCustomMigrationStage: 0x107220990>
SwiftData/PersistentModel.swift:734: Fatal error: What kind of backing data is this? SwiftData._KKMDBackingData<Impact.ImpactSchemaV8.TrajectoryMode>

This time, the app is crashing on a different property than the previous crash...

I have some potential progress, but due to the nature of this crash, I don't know if this is correct or not.

After inspecting the sqlite-files that represents the backingdata, I discovered that a property backed by an integer was set to null after migration even the original value was 3.

Upon inspection, I realized this was the very first property I ever migrated, so it was still annotated with @Attribute(orginalName: "propertyName"), even tho the migration which crashed for me was the 9th migration step overall for the app.

Thus, with the Schema I was migrating from being:

struct SchemaV7 {
    @Model
    class Item {
        @Attribute(originalName: "propertyName")
        var newPropertyName: Int
    }
}

And then removing it in the destination Schema:

struct SchemaV8 {
    @Model
    class Item {
        var newPropertyName: Int
    }
}

made me able to complete the migration without crashing.

Wether this was the solution or not, I have no idea, but it could explain some of the crashes I've seen earlier...

The property which was crashing the app have never been optional, but the sqlite column driving the backingstore was set to optional, which explains the crash. It doesn't however explain the data loss on migration

SwiftData crash on fetch
 
 
Q