19
votes

A handful of customers for my iPhone app are experiencing Core Data store corruption (I assume so, since the error is "Failed to save to data store: Operation could not be completed. (Cocoa error 259.)")

Has anyone else experienced this kind of store corruption? I am worried since I aim to soon push an update which performs a schema migration, and I am worried that this will expose even more problems.

I had assumed that the Core Data/SQLlite APIs use atomic operations and are immune to corruption except if the underlying filesystem experiences corruption.

Is there a way to reduce/prevent corruption, and a way to reproduce the corruption so I can test this (I have been unsuccessful thus far).

Edit:

Also getting this error: "The database at /var/mobile/Applications//Documents/foo.sqlite is corrupted. SQLite error code 11, database disk image is malformed."

8
Also getting this error: "The database at /var/mobile/Applications/<UUID>/Documents/foo.sqlite is corrupted. SQLite error code 11, database disk image is malformed." - sehugg
Sounds like this could be due to a change in the SQLite lib included in 4.2 vs 4.3: devforums.apple.com/message/409734 - Hunter
I've been getting similar reports from users of my app, especially users using older devices with less hardware power. The only fix I have in place now is when a corrupt store is detected it is deleted, recreated, then data is repopulated from the server which far from optimal. - Jiho Kang
Also good to note that I have a lot of background thread performing Core Data operations. Might have something to do with older devices not support multitasking where apps are killed immediately. I'm wondering what happens to my background threads when applicationWillTerminate is called. - Jiho Kang
I have a feeling bad stuff happens if you have threads doing Core Data stuff after your app exits. It might be wise to put your stuff into a NSOperationQueue and start a background task for pending operations when the app exits -- at least you'll have a few seconds to finish everything. - sehugg

8 Answers

21
votes

It happens to me when I manually overwrote my Base.sqlite without deleting Base.sqlite-wal and Base.sqlite-shm. Indeed, these files are new SQLite 3.7 features, maybe added in iOS 7.

To resolve the problem, I deleted Base.sqlite-* and sqlite regenerated them from my new base version.

2
votes

Also attempt to replicate the error by filling the drive on the Device -- you should receive a disk full error instead of the database corrupting, but it may be possible to get a corrupted database in this manner.

2
votes

The error you're getting is defined in Foundation.h

NSFileReadCorruptFileError = 259, // Read error (file corrupt, bad format, etc)

I've never encountered it with an actual store but I have hit something similar with bad permissions (on the Mac.) I haven't seen anyone mention a similar error online either. The error prevention systems in Core Data are fairly robust.

I would guess that the easiest way to create this would be send the persistent store to look at the wrong file such as accidentally targeting it at a text file. If it expects an SQL store but finds something else it will complain that the file is corrupt. That's just a shot in the dark.

Edit

This will be hard to track down because errors like this are so rare in Core Data that there aren't any tools to assist finding the problem.

I would recommend:

  1. Checking upstream of where the error is coming from code. Perhaps something is throwing the store off or is causing it to look in another place.
  2. Check anywhere you might do something non-standard. For example, if you generate your own entity map in code, its easy to throw it off if your not careful.
2
votes

For clarity, using Xcode 7.2.1, SQLite data store, Core Data object graph, for a prototype app.

My problem was detailed by the Xcode terminal as:

CoreData: error: (11) Fatal error. The database at /Users/etc/Library/Developer/CoreSimulator/Devices/etc/data/Containers/Data/Application/etc/Library/Application Support/com.etc.etc/etc.sqlite is corrupted. SQLite error code:11, 'database disk image is malformed'.

Effectively my app was able to load and read the SQLite data, but was unable to save.

This answer by SO user software evolved made sense to me. While using Simulator I was fairly certain I had interrupted a save operation on a managed object context with private queue concurrency type NSPrivateQueueConcurrencyType.

Further investigation (using SQLiteManager) revealed the SQLite table I had been saving to at the time was the cause of this problem.

I could have easily deleted the app (no public release yet) however I wanted to understand at least how to repair this problem.

Notes from this experience:

  • In the app delegate under the UIApplicationDelegate Protocol - (void)applicationWillResignActive:(UIApplication *)application, if your managed object context hasChanges be sure to include a database save method;
  • If using more than one queue, use the Home button to trigger the delegate method in item 1 before clicking the stop button in Xcode;
  • Repair the damaged database file - I developed a solution detailed below from the answer outlined on this webpage Fixing the SQLite error “The database disk image is malformed”.

SQLite Database File Repair Method:

  1. Open terminal [terminal/sqlite commands];
  2. Navigate to the appropriate file location [cd];
  3. For backup purposes, make a copy of the 'malformed' database file (e.g.dbMalFormedBU.sqlite) (that can be deleted later if the repair is successful) [cp];
  4. To be certain, delete the dbMalFormed.sqlite-shm and dbMalFormed.sqlite-wal files [rm];
  5. Open your 'malformed' database file [sqlite3 dbMalFormed.sqlite];
  6. Clone your database file [.clone dbMalFormedNew.sqlite];
  7. Exit SQLite3 [.exit];
  8. Delete the old 'malformed' database file [rm dbMalFormed.sqlite);
  9. Rename the new database file to the name used previously [mv dbMalFormedNew.sqlite dbMalFormed.sqlite].
2
votes

I experienced that error when trying to get the persistent store coordinator.

Multi-threading was the problem in my case. Fixed it wrapping the whole method with a @synchronized(self) {} block.

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    @synchronized(self) {

    // Quickly return persistent store coordinator if available
    if (__persistentStoreCoordinator != nil) {
        return __persistentStoreCoordinator;
    }

    // Persistent store coordination initialization, to be performed once
    // ...

    }
    return _persistentStoreCoordinator;
}
1
votes

Are you ever interacting with the database using the sqlite API? Or have you used any non-Apple tools to create your seed database?

1
votes

I experienced "Core Data store corruption" when I was preloading the database with a separated thread(not main thread) and it has been really difficult to find this bug since only a few of my customers will crash their app because of this.

0
votes

I recently ran across this problem. In my case I was performing a search and iterating over the objects to transform the data to XML and KML. I would then spawn the email handler and attach the files. Then I would update a field in the objects and finally save to the backing store (SQL Lite). The worked fine in 3.x. In 4.x it broke.

It was stupid on my part to do all the email handling before altering and saving the DB. Moving all non essential code to the point after the save cleared up this problem.

This problem would totally corrupt the SQL DB. In my case the error is:

 File at path does not appear to be a SQLite database