modelContext.fetch() hits assert on release builds, but not on debug builds

Exact same app works fine in debug builds, but on release builds I see this stacktrace indicating that assert() was hit.

Incident Identifier: ***
Distributor ID:      com.apple.TestFlight
Hardware Model:      iPhone14,3
Process:             AuditOS [67847]
Path:                /private/var/containers/Bundle/Application/***
Identifier:          ***
Version:             1.0 (15)
AppStoreTools:       16C5031b
AppVariant:          1:iPhone14,3:18
Beta:                YES
Code Type:           ARM-64 (Native)
Role:                Foreground
Parent Process:      launchd [1]
Coalition:           ***

Date/Time:           2025-02-11 12:37:54.7801 -0600
Launch Time:         2025-02-11 12:37:33.1737 -0600
OS Version:          iPhone OS 18.3 (22D63)
Release Type:        User
Baseband Version:    4.20.03
Report Version:      104

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

Triggered by Thread:  0


Thread 0 Crashed:
0   libswiftCore.dylib            	0x000000019d388e2c _assertionFailure(_:_:file:line:flags:) + 264 (AssertCommon.swift:147)
1   SwiftData                     	0x0000000261842e04 Schema.KeyPathCache.validateAndCache(keypath:on:) + 2628 (Schema.swift:0)
2   SwiftData                     	0x000000026178cac4 static PersistentModel.keyPathToString(keypath:) + 360 (DataUtilities.swift:36)
3   SwiftData                     	0x000000026184c9e4 static PersistentModel.fetchDescriptorKeyPathString(for:) + 36 (FetchDescriptor.swift:51)
4   SwiftData                     	0x00000002617b9770 closure #1 in PredicateExpressions.KeyPath.convert(state:) + 172 (FetchDescriptor.swift:458)
5   SwiftData                     	0x00000002617b7f48 PredicateExpressions.KeyPath.convert(state:) + 352 (FetchDescriptor.swift:438)
6   SwiftData                     	0x00000002617bb7ec protocol witness for ConvertibleExpression.convert(state:) in conformance PredicateExpressions.KeyPath<A, B> + 16 (<compiler-generated>:0)
7   SwiftData                     	0x00000002617baaa0 PredicateExpression.convertToExpressionOrPredicate(state:) + 716 (FetchDescriptor.swift:219)
8   SwiftData                     	0x00000002617ba6dc PredicateExpression.convertToExpression(state:) + 32 (FetchDescriptor.swift:237)
9   SwiftData                     	0x00000002617b7cfc PredicateExpressions.Equal.convert(state:) + 328 (:-1)
10  SwiftData                     	0x00000002617bba08 protocol witness for ConvertibleExpression.convert(state:) in conformance PredicateExpressions.Equal<A, B> + 64 (<compiler-generated>:0)
11  SwiftData                     	0x00000002617baaa0 PredicateExpression.convertToExpressionOrPredicate(state:) + 716 (FetchDescriptor.swift:219)
12  SwiftData                     	0x00000002617b7abc PredicateExpression.convertToPredicate(state:) + 28 (FetchDescriptor.swift:244)
13  SwiftData                     	0x00000002617b7190 nsFetchRequest<A>(for:in:) + 1204 (FetchDescriptor.swift:64)
14  SwiftData                     	0x0000000261783358 DefaultStore.fetch<A>(_:) + 292 (DefaultStore.swift:496)
15  SwiftData                     	0x000000026178322c protocol witness for DataStore.fetch<A>(_:) in conformance DefaultStore + 16 (<compiler-generated>:0)
16  SwiftData                     	0x00000002617847fc asDataStore #1 <A><A1>(_:) in closure #1 in ModelContext.fetch<A>(_:) + 3152 (ModelContext.swift:2590)
17  SwiftData                     	0x00000002617a74d8 partial apply for closure #1 in ModelContext.fetch<A>(_:) + 100 (<compiler-generated>:0)
18  SwiftData                     	0x00000002617a7438 closure #1 in ModelContext.enumerateFetchableStores<A>(_:_:) + 208 (ModelContext.swift:2527)
19  SwiftData                     	0x00000002617a731c specialized ModelContext.enumerateFetchableStores<A>(_:_:) + 200 (ModelContext.swift:2522)
20  SwiftData                     	0x00000002617a6f08 ModelContext.fetch<A>(_:) + 144 (ModelContext.swift:2534)
21  SwiftData                     	0x00000002617a6e70 dispatch thunk of ModelContext.fetch<A>(_:) + 56 (:-1)
22  AuditOS                       	0x00000001041af3f4 0x10419c000 + 78836
23  AuditOS                       	0x00000001041bebd5 0x10419c000 + 142293
24  AuditOS                       	0x00000001041bbbf5 0x10419c000 + 130037
25  AuditOS                       	0x00000001041d8be5 0x10419c000 + 248805
26  AuditOS                       	0x00000001041bde6d 0x10419c000 + 138861
27  libswift_Concurrency.dylib    	0x00000001aa6bfe39 completeTaskWithClosure(swift::AsyncContext*, swift::SwiftError*) + 1 (Task.cpp:497)

The code in question looks like this:

    func addRecord<T: MyDtoProtocol>(_ someDTO: T) async throws {
        var zone: ZoneModel? = nil

        let recordName = someDTO.recordNameType
        let fetchDescriptor = FetchDescriptor<T.ModelType> (predicate: #Predicate {$0.recordName == recordName})

>         var localEntitites: [T.ModelType] = try modelContext.fetch(fetchDescriptor) <---- I have isolated crash to this line.

Basically for each swiftdata model type I have associatedType for Data Transfer Object type and vice versa.

Answered by DTS Engineer in 824850022

Exact same app works fine in debug builds, but on release builds I see this stacktrace indicating that assert() was hit.

I think this is worth a bug report. Did you file a feedback report yet? If yes, would you mind to share your report ID here for folks to track? Thanks.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Instead of using addRecord with generics I switched to concrete type and it is not hitting assert() anymore:

    func addWorkspace(_ someDTO: WorkspaceDTO) async throws {
        var zone: ZoneModel? = nil

        let recordName = someDTO.recordNameType
        let fetchDescriptor = FetchDescriptor<WorkspaceModel> (predicate: #Predicate {$0.recordName == recordName})

         var localEntitites: [WorkspaceModel] = try modelContext.fetch(fetchDescriptor) <---- no more crash here

So it seems that Fetch Descriptor crashes only if I use it with generics. Not with concrete types. Anyway, seems like a bug in swiftData.

How do you know T.ModelType has a property recordName?

Accepted Answer

Exact same app works fine in debug builds, but on release builds I see this stacktrace indicating that assert() was hit.

I think this is worth a bug report. Did you file a feedback report yet? If yes, would you mind to share your report ID here for folks to track? Thanks.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

I’ve uploaded reproduction instructions to FB16485212, including a minimal patch that needs to be applied to Xcode’s default new project template.

Thanks for providing reproduction instructions, which are important.

Is there a way to check if someone from Apple Engineering is reviewing this issue?

Other than the current status of your report in Feedback Assistant, our feedback system doesn't share more information. For now, I see that your report has been assigned to the SwiftData team.

Just so you know, we don’t guarantee to reply every feedback. The team will investigate the issue and decide what to do. They typically only bother a feedback reporter when they need to gather more information or have something to communicate.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

modelContext.fetch() hits assert on release builds, but not on debug builds
 
 
Q