SwiftData - Context missing for optional

I have encountered an issue that when using a ModelActor to sync data in the background, the app will crash if one of the operations is to remove a PersistentModel from the context.

This is running on the latest beta of Xcode 16 with visionOS 1.2 as target and in Swift 6 language mode.

The code is being executed in a ModelActor.

The error is first thrown by: #5 0x00000001c3223280 in PersistentModel.getValue<τ_0_0>(forKey:) ()

Thread 1: Fatal error: Context is missing for Optional(SwiftData.PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata://97AA86BC-475D-4509-9004-D1182ABA1922/Reminder/p303), implementation: SwiftData.PersistentIdentifierImplementation))

func globalSync() async {
        
        await fetchAndSyncFolders()
        
        let result = await fetchReminders()
        switch result {
        case .success(let ekReminders):
            var localReminders = (try? await fetch(FetchDescriptor<Reminder>())) ?? []
            
            // Handle local reminders with nil ekReminderID by creating new EKReminders for them
            for reminder in localReminders {
                if reminder.ekReminderID == nil {
                    await self.createEkReminder(reminder: reminder)
                }
            }
            
            // Re-fetch local reminders to include newly created EKReminderIDs
            localReminders = (try? await fetch(FetchDescriptor<Reminder>())) ?? []
            
            var localReminderDict = [String: Reminder]()
            for reminder in localReminders {
                if let ekReminderID = reminder.ekReminderID {
                    if let existingReminder = localReminderDict[ekReminderID] {
                        self.delete(model: existingReminder)
                    } else {
                        localReminderDict[ekReminderID] = reminder
                    }
                }
            }
            
            let ekReminderDict = createReminderLookup(byID: ekReminders)
            
            await self.syncReminders(localReminders: Array(localReminderDict.values), localReminderDict: localReminderDict, ekReminderDict: ekReminderDict)
            
            // Merge duplicates
            await self.mergeDuplicates(localReminders: localReminders)
            
            save()
            
        case .failure(let error):
            print("Failed to fetch reminders: \(error.localizedDescription)")
        }
    }
Answered by DTS Engineer in 797958022

Thread 1: Fatal error: Context is missing for Optional(SwiftData.PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata://97AA86BC-475D-4509-9004-D1182ABA1922/Reminder/p303), implementation: SwiftData.PersistentIdentifierImplementation))

I am pretty sure that simply deleting a model object using (or not) a model actor (ModelActor) doesn't trigger the mentioned crash.

This error indicates that your model object doesn't have a valid model context, which can happen when you hold a SwiftData model object in a SwiftUI view and continue to access the object (as the view is updated) after it was deleted.

If that is not your case, please share a minimal project, with detailed steps, to reproduce the issue. I'll be interested in taking a look.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

I am also experiencing the same issue.

It works fine on the actual iPhone after updating to iOS 18, but when deleting in the Mac Catalyst app and calling try ctx.save(), the app crashes with the error 'SwiftData/BackingData.swift:251: Fatal error: Context is missing for Optional.'

Thank you for your hard work! 👍

@DTS Engineer

Hi, do you have an update on this please? I'm getting crashes from sessions in production from customers on iOS 18.0. This appears to still be happening Beta 5 of iOS 18.1. Let me know if I can provide more information beyond what others have shared in the thread. Thank you so much!

I'm seeing the same crash after updating iPhone to iOS18. It doesn't crash the sim, which is iOS17 for me. Also a very weird thing is that if I don't call saveContext() is don't crash (but the context does seem to update and look like the record is deleted??). Thankfully I haven't released app to prod yet. This is quite worrying. I've spent a lot time trying different way of passing in a context. This doesn't happen on iOS 17.

this seem to work in my delete function, but shouldn't I have to save the context? container.mainContext.delete(recording) // saveContext()

SwiftData/BackingData.swift:251: Fatal error: Context is missing for Optional(SwiftData.PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata://2B4AE978-7D52-4789-A52A-4593CEBF5690/Recording/p27), implementation: SwiftData.PersistentIdentifierImplementation))

Nevermind... sorry. not saving the context means that on reload the record hasn't actually been deleted. I guess this is now a launch blocker as crashing every time something is deleted is extremely bad. :^(

I have found a solution, at least one that works for my project. I hope it can help others here!

The issue wasn't in the code I had to delete the record from the model context. The problem was an improper use of Binding.

A parent view was presenting a list of child views. Into that child view I was passing a "Recording". Within the child that Recording was an at Binding. That was totally unnecessary and I think causes a conflict with the model context being used by the child(?). So it was being passed in as a constant like this:

                    List {
                        ForEach(filteredRecordings) { recording in
                            RecordingItemView(
                                recording: .constant(recording), 

Removing the Binding and changing a regular property fixed my iOS18 crash:

List { ForEach(filteredRecordings) { recording in RecordingItemView( recording: recording,

The UI of my parent view is already being updated via changes to the modelContext... so the Binding is just a total mistake.

Good luck!

hey I can confirm it happens when calling save after delete. Doing the context save after a short delay seems to work for now


Button {
    modelContext.delete(occurrence)
    dismiss()
    Task {
        try? await Task.sleep(nanoseconds: 1_000_000_000)
        do {
            try modelContext.save()
        } catch {
            print(error.localizedDescription)
        }
    }
} label: { 
    Text("Delete Occurence") 
}

This error indicates that your model object doesn't have a valid model context, which can happen when you hold a SwiftData model object in a SwiftUI view and continue to access the object (as the view is updated) after it was deleted.

In my case it was exactly as @DTS Engineer pointed out. One of my parent views State properties was holding a model object where the relationship property was a deleted model object. That relationship was still accessed in the parent view and lead to the crash. Explicitly setting those states to nil helped resolving the issue.

It took some time to identify where the issue happened exactly. What helped was the backtrace of the main thread inside the Debug Navigator. Hope that helps.

I have had similar problems when moving to macOS 15 (for a Mac app). The issues seem to have been resolved in macOS 15.1 beta 5.

Hi everyone! Stumbled upon the same issue recently. When trying to delete an object from its DetailsPage - getting a crash "Fatal error: Context is missing for"

It seems to be very much related to what properties of the object are being used on the View that suppose to disappear after deletion. DetailsPage in my case, but it can be a ListItemView just as well.

What I figured, that there are 3 things that together lead to this crash:

  1. The View that "holds" the object must use some of the object's relationship(s) propertiers. For example, count of children objects, or the name of the parent object, etc.
  2. There must be try context.save() after deletion of model object.
  3. It must be iOS 18. Yes, this behavior is new. More on that later.

Test setup

Is quite simple. You can download SampleTrips SwiftData app from AppleDeveloper

Navigate to BucketListView.swift and modify a little a struct BucketListItemToggle so that it displays some of the item's parent values.

In order to see the crash in action easier, I'd also suggest to make bucket-list-item deletion upon button press (not swipe action).

With swipe-action, crash is also reproducible - just add try? modelContext.save() to deleteItems(at:). The crash will happen not exactly after deletion, but after subsequent addition of new bucket-list-item. This is probably related to the way how swipeAction removes the view from hierarchy.

Run on iOS18 device OR simulator. Create a Trip, go to Trip details > View Bucket List > Create a BucketListItem (feel free to create several) > Press on any item. Observe the crash.

Note: with this demo project setup, crash happens in about 80% cases when deleting an item. Suspicion is that the more "relational-properties" the child view consumes - the higher chance for a crash.

PS: I've also added Text("\(item.persistentModelID)") to the item row, just to see that the crash indeed happens because of the view being deleted.

Explanation (at least an attempt)

As you can see, crash happens upon trying to access trip? property of the item. This is a "relational" (parent) property of the BucketListItem model.

👉 Seems like iOS18 started to be more strict when accessing parent/children properties when the object itself was removed from the context.

Indeed, if we do po item.modelContext in that breakpoint - we will see that it is nil. In previous iOS versions, it would just fallback and return nil when asking for such trip? property. But on iOS18 - it seems to try to unfault a relation, sees that context is missing and throws a fatal error...

Now, to the question why the view which should disappear is trying to refresh? Here is just my assumption based on observation of Self._printChanges(). Basically that is how SwiftUI always worked. For one reason or another, it recognized a change and needed to refresh the view before parent view recognized that this view should no longer exist in the first place...

Possible solutions

There are some options that I guess one can try:

  1. Consider removing context.save(). If you have a context with autosaveEnabled -- forcing it to save might not be necessary. This seems to give SwiftUI some time to refresh the "view-being-deleted" without an issue. Then parent view will recognise the change and remove the child view completely as expected.
  2. Don't use "relational-properties" in child views. This is much easier said than done. You can pass all relevant values "from above" and make you child views as discrete as possible.
  3. Add if object.modelContext == nil { EmptyView() } else { ... } in bodies of your views which object can get removed.
  4. Simulate fallback to pre-iOS18 by writing safe getters and use them in views to read data from relationships. E.g. var safeTrip: Trip? { guard modelContext != nil else { return nil }; return trip }

For my project, I was lucky with being able to use option 1. But I'm considering adding safe getters anyway just in case 🫣

Would be awesome to hear from Apple if our observations are correct or not 🤞

Thank you all & have fun coding 🙂

My solution to this so far was listening to onDisappear method of an item in the ForEach and calling try? modelContext.save() manually. Autosave is not disabled and everything else works as default config.

Hope this helps :)


// Parent view
ForEach(items) { item in
    ItemsGrid(item: item)
        .onTapGesture {
            markAsCompleted(item)
         }
         .modelContext(modelContext)
         .onDisappear {
              if (item.isDeleted) {
                  print("Item deleted: \(item.isDeleted)")
                       try? modelContext.save()
                   }    
               }
          }



// In Item Detail View: 
// I'm only calling this to trigger onDisappear method
private func deleteItem() {
    modelContext.delete(habit)
}

Thank you so much for your suggested solutions @Nevs! The third option that check modelContext == nil seems to work for me on iOS 18.

For example this works for me

var body: some View {
    Text("The school has \(school.students.count) students")
    Button("Delete school", action: {
        context.delete(school)
        try! context.save()
    })
}
@Model SchoolModel {
    @Relationship(deleteRule: .cascade, inverse: \StudentModel.school)
    var swiftDataStudents: [StudentModel]? = []
}

    var students: [StudentModel] {
        set { swiftDataStudents = newValue }
        get {
            if self.modelContext == nil {
                return []
            }
            return swiftDataStudents ?? []
        }
    }

I had the same issue when trying to remove a child object from a relationship directly using the context.delete method

I fixed my crash by removing the object from the parent object list and saving the context.

So instead of doing

mainModelContext.delete(child)

I am doing

// get the child index from the parent
parent.childs.remove(at: childIndex)
mainModelContext.save()

This seems to fix the crash and its also trigger update on the parent model if a view is observing it.

My experience with SwiftData so far has been that if you have to modify relationships it's better to do it from parent side. I have encountered crashes or updates not triggering as expected otherwise.

SwiftData - Context missing for optional
 
 
Q