How to debug this?

I have a small app written in SwiftUI and using SwiftData. It's been performing well for around a month but now suddenly I am getting the error below.

No changes have been made to the models in use. Looking at the error it appears to be when SwiftData commits data but how can I tell what it is trying (and failing) to commit? Some way of knowing the model being referenced would help me diagnose this further. The error occurs at random intervals, though I'm guessing that's partly reflected by the design of SwiftData?

[error] error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x60000376af40> , -[__NSDictionaryM UTF8String]: unrecognized selector sent to instance 0x60000022adc0 with userInfo of (null)

CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x60000376af40> , -[__NSDictionaryM UTF8String]: unrecognized selector sent to instance 0x60000022adc0 with userInfo of (null)

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryM UTF8String]: unrecognized selector sent to instance 0x60000022adc0' *** First throw call stack:

( 0 CoreFoundation 0x00000001804ae138 __exceptionPreprocess + 172 1 libobjc.A.dylib 0x0000000180087db4 objc_exception_throw + 56 2 CoreFoundation 0x00000001804c2f88 +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0 3 CoreFoundation 0x00000001804b2288 forwarding + 1280 4 CoreFoundation 0x00000001804b45ac _CF_forwarding_prep_0 + 92 5 CoreData 0x0000000186537b90 -[NSSQLiteConnection execute] + 1104 6 CoreData 0x0000000186534b58 -[NSSQLiteConnection updateRow:forRequestContext:] + 1848 7 CoreData 0x0000000186634378 _executeSaveChangesRequest + 2216 8 CoreData 0x000

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryM UTF8String]: unrecognized selector sent to instance 0x60000022adc0' *** First throw call stack: ( 0 CoreFoundation 0x00000001804ae138 __exceptionPreprocess + 172 1 libobjc.A.dylib 0x0000000180087db4 objc_exception_throw + 56 2 CoreFoundation 0x00000001804c2f88 +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0 3 CoreFoundation 0x00000001804b2288 forwarding + 1280 4 CoreFoundation 0x00000001804b45ac _CF_forwarding_prep_0 + 92 5 CoreData 0x0000000186537b90 -[NSSQLiteConnection execute] + 1104 6 CoreData 0x0000000186534b58 -[NSSQLiteConnection updateRow:forRequestContext:] + 1848 7 CoreData 0x0000000186634378 _executeSaveChangesRequest + 2216 8 CoreData 0x000000018661e5e8 -[NSSQLSaveChangesRequestContext executeRequestCore:] + 28 9 CoreData 0x000000018641b28c -[NSSQLStoreRequestContext executeRequestUsingConnection:] + 248 10 CoreData 0x00000001864b4df4 __52-[NSSQLDefaultConnectionManager handleStoreRequest:]_block_invoke + 56 11 CoreData 0x000000018652d790 __37-[NSSQLiteConnection performAndWait:]_block_invoke + 40 12 libdispatch.dylib 0x00000001016e573c _dispatch_client_callout + 16 13 libdispatch.dylib 0x00000001016f648c _dispatch_lane_barrier_sync_invoke_and_complete + 144 14 CoreData 0x000000018652d248 -[NSSQLiteConnection performAndWait:] + 132 15 CoreData 0x00000001864b4d14 -[NSSQLDefaultConnectionManager handleStoreRequest:] + 204 16 CoreData 0x00000001865f29f4 -[NSSQLCoreDispatchManager routeStoreRequest:] + 220 17 CoreData 0x00000001864fa280 -[NSSQLCore dispatchRequest:withRetries:] + 168 18 CoreData 0x00000001864fc9e4 -[NSSQLCore executeRequest:withContext:error:] + 2116 19 CoreData 0x00000001864c6a84 __65-[NSPersistentStoreCoordinator executeRequest:withContext:error:]_block_invoke.383 + 9184 20 CoreData 0x00000001864bbac4 -[NSPersistentStoreCoordinator _routeHeavyweightBlock:] + 216 21 CoreData 0x00000001864c3ec8 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 1100 22 CoreData 0x000000018647c8f8 -[NSManagedObjectContext save:] + 968 23 SwiftData 0x00000001cb408450 _swift_destroy_boxed_opaque_existential_0Tm + 50180 24 SwiftData 0x00000001cb4168d4 block_destroy_helper + 132 25 CoreData 0x00000001863cfec4 $sxs5Error_pIgrzo_xsAA_pIegrzo_lTR + 20 26 CoreData 0x00000001863d2198 $sxs5Error_pIgrzo_xsAA_pIegrzo_lTRTA + 20 27 CoreData 0x00000001863d1c8c $sSo22NSManagedObjectContextC8CoreDataE30_rethrowsHelper_performAndWait33_A0F63BA25A751D403694B89F4AD0A24ELL2fn7execute6rescuexyyyXEXE_xyKXExs5Error_pKXEtKlFyxyKcXEfU_yyXEfU + 108 28 CoreData 0x00000001863d04a4 $sIg_Ieg_TR + 20 29 CoreData 0x00000001863d04f4 $sIeg_IyB_TR + 20 30 CoreData 0x000000018648584c developerSubmittedBlockToNSManagedObjectContextPerform + 156 31 CoreData 0x0000000186485728 -[NSManagedObjectContext performBlockAndWait:] + 212 32 CoreData 0x00000001863d203c $sSo22NSManagedObjectContextC8CoreDataE14performAndWaityxxyKXEKlF + 352 33 SwiftData 0x00000001cb407b60 __swift_destroy_boxed_opaque_existential_0Tm + 47892 34 SwiftData 0x00000001cb4077a8 __swift_destroy_boxed_opaque_existential_0Tm + 46940 35 SwiftData 0x00000001cb40789c __swift_destroy_boxed_opaque_existential_0Tm + 47184 36 Foundation 0x0000000180e12924 __NSFireTimer + 56 37 CoreFoundation 0x000000018040f588 CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION + 28

Suggestions for narrowing down the cause gratefully accepted :-)

Answered by DTS Engineer in 788679022

SwiftData's default backing store (BackingData) is based on Core Data. When I see a random crash that seems to be related to an invalid memory address and occurs deeply in Core Data, the first thing I’d check is if the app runs into a Core Data concurrency issue.

With Xcode, you can use -com.apple.CoreData.ConcurrencyDebug 1 as a launch argument to do the check. Note that the argument has the “-“ prefix. To do so:

  1. Open your project with Xcode.
  2. Click the target in the middle of Xcode’s title bar to show the drop list, and then click “Edit Schema...” to show the schema editing dialog.
  3. Add the argument to the “Arguments Passed on Launch” list, as shown in the attached screenshot.
  4. Run your app and try to reproduce the issue.

If your app hits a concurrency violation, the debugger halts at the following symbol: +[NSManagedObjectContext Multithreading_Violation_AllThatIsLeftToUsIsHonor].

If that is indeed the case, one common reason is that you read or write a main-context model object – an object whose modelContext is mainContext – from a non-main queue, and the solution is to access the object from the main queue, because mainContext is bound to the main queue.

SwiftData's default backing store (BackingData) is based on Core Data. When I see a random crash that seems to be related to an invalid memory address and occurs deeply in Core Data, the first thing I’d check is if the app runs into a Core Data concurrency issue.

With Xcode, you can use -com.apple.CoreData.ConcurrencyDebug 1 as a launch argument to do the check. Note that the argument has the “-“ prefix. To do so:

  1. Open your project with Xcode.
  2. Click the target in the middle of Xcode’s title bar to show the drop list, and then click “Edit Schema...” to show the schema editing dialog.
  3. Add the argument to the “Arguments Passed on Launch” list, as shown in the attached screenshot.
  4. Run your app and try to reproduce the issue.

If your app hits a concurrency violation, the debugger halts at the following symbol: +[NSManagedObjectContext Multithreading_Violation_AllThatIsLeftToUsIsHonor].

If that is indeed the case, one common reason is that you read or write a main-context model object – an object whose modelContext is mainContext – from a non-main queue, and the solution is to access the object from the main queue, because mainContext is bound to the main queue.

Thanks for the detailed response!

I tried the setting but it didn't produce anything new :-( I then tried adding -com.apple.CoreData.SQLDebug 1 in an effort to try and see what was going on. This shows the SQL commands so I can at least what was being done prior to the crash.

I can see the model that is last updated via the SQL but can't see what is being inserted. Is there a command to also see the data being inserted/updated rather than just the SQL commands?

Having increased the SQLDebug level to 2 I can see the data :-)

The model that is causing the issue has an enum, and the sqlite schema seems to have an entry for each possible case? There are 3 update or fail queries made in a row, all with the same enum case being set. In the first the appropriate lines for the query data look like this,

CoreData: details: SQLite bind[11] = "{
}"
CoreData: details: SQLite bind[12] = nil
CoreData: details: SQLite bind[13] = nil
CoreData: details: SQLite bind[14] = nil
CoreData: details: SQLite bind[15] = nil
CoreData: details: SQLite bind[16] = nil
CoreData: details: SQLite bind[17] = nil
CoreData: details: SQLite bind[18] = nil
CoreData: details: SQLite bind[19] = nil
CoreData: details: SQLite bind[20] = nil

For the query that appears to trigger the error, I have this...

CoreData: details: SQLite bind[11] = {
}
CoreData: sql: ROLLBACK

Obviously this makes a degree of sense for the error as the value being set isn't a string? Progress!

Having commented out all code that changes the enum value, so it is simply set to the initial value at model creation the error still appears - which is puzzling me. Additionally the annotation showing the old and new row data show the field has not been changed (in fact it shows no changed fields at all). I'm at a loss to know why this is throwing the error?

If I remove the enum from the model I don't experience the crash.

Any suggestions what's going on or how to debug/fix this further?

If the error is simply triggered by a Query, I'll be wondering if you are using a predicate, and if the argument passed to the predicate has the right type. An argument with a wrong type might trigger an NSInvalidArgumentException.

Other than that, it is pretty strange to me that the error happened when the framework was trying to save changes, while your code didn't change anything. If you can provide a focused sample that reproduces the issue, I may be able to take a closer look when I catch a chance.

Apologies for the delay in responding.

Removing the enum allowed me to find another issue whereby model insertion seemed to be happening in a strange order. Adding some direct calls to modelContext.save() cured that.

I'm still a little confused though as the model concerned sets the enum value as a default, yet when the inserts were being done they seemed to use a totally uninitialized model object with none of the default values assigned. Is this expected?

However, all now works as expected and my debugging knowledge has improved :-)

With Xcode, you can use -com.apple.CoreData.ConcurrencyDebug 1 as a launch argument to do the check.

By any chance do we know if there are any known issues that might lead to false positive errors when using the ConcurrencyDebug argument with SwiftData? I am seeing some concurrency errors with a SwiftData stack… but I'm looking through how this stack is set up and I can't understand what could be leading to any kind of race condition.

Ahh… I don't know how to delete this comment and move it to a thread. Sorry about that!

How to debug this?
 
 
Q