1

With my most recent app update, I have started to see very inconsistent SQLite errors when saving my database. These are happening with multiple users, so it is not just the same user crashing repeatedly (though it has happened for the same user multiple times). I am getting error 266, which is SQLITE_IOERR_READ. I haven't found anyone else running into this error, so not sure why I'm getting it.

00:04:18:25  $ -[AppDelegate saveContext] line 328 $ Unresolved error Error Domain=NSCocoaErrorDomain Code=266 "The operation couldn’t be completed. (Cocoa error 266.)" UserInfo=0x1dd141b0 {NSSQLiteErrorDomain=266, NSFilePath=/var/mobile/Applications/[omitted], NSPOSIXErrorDomain=1, NSUnderlyingException=I/O error for database at /var/mobile/Applications/[omitted]. SQLite error code:266, 'not an error' errno:1}, {
* 00:04:18:25  NSFilePath = "/var/mobile/Applications/[omitted].sqlite";
* 00:04:18:25  NSPOSIXErrorDomain = 1;
* 00:04:18:25  NSSQLiteErrorDomain = 266;
* 00:04:18:25  NSUnderlyingException = "I/O error for database at /var/mobile/Applications/[omitted].sqlite. SQLite error code:266, 'not an error' errno:1";
* 00:04:18:25  }

EDIT

Here is the core-data related code (most of it standard boilerplate):

/**
 Returns the managed object context for the application.
 If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
 */
- (NSManagedObjectContext *) managedObjectContext {

    if (managedObjectContext != nil) {
        return managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        managedObjectContext = [[NSManagedObjectContext alloc] init];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    }
    return managedObjectContext;
}


/**
 Returns the managed object model for the application.
 If the model doesn't already exist, it is created by merging all of the models found in the application bundle.
 */
- (NSManagedObjectModel *)managedObjectModel {

    if (managedObjectModel != nil) {
        return managedObjectModel;
    }
    //managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];

    // See  http://iphonedevelopment.blogspot.com.au/2009/09/core-data-migration-problems.html
    NSString *path = [[NSBundle mainBundle] pathForResource:@"modelDB" ofType:@"momd"];
    NSURL *momURL = [NSURL fileURLWithPath:path];
    managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];

    return managedObjectModel;
}


/**
 Returns the persistent store coordinator for the application.
 If the coordinator doesn't already exist, it is created and the application's store added to it.
 */
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }

    NSString *storePath = [[Utils documentsDirectory] stringByAppendingPathComponent: @"modelDB.sqlite"];
    NSURL *storeUrl = [NSURL fileURLWithPath: storePath];

    NSError *error = nil;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];

    // Allow inferred migration from the original version of the application.
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
        // Handle the error.
        CLS_LOG(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    //Turn on complete file protection (encrypts files when phone is locked using device pin)
    NSDictionary *fileAttributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
    if(![[NSFileManager defaultManager] setAttributes:fileAttributes ofItemAtPath:storePath error:&error])
    {
        //handle error
    }

    return persistentStoreCoordinator;
}

When a user logs out, this is called to remove the model store:

- (NSPersistentStoreCoordinator *)resetPersistentStore
{
    NSError *error = nil;

    if ([persistentStoreCoordinator persistentStores] == nil)
        return [self persistentStoreCoordinator];

    [managedObjectContext release];
    managedObjectContext = nil;

    //If there are many stores, this could be an issue
    NSPersistentStore *store = [[persistentStoreCoordinator persistentStores] lastObject];

    if (![persistentStoreCoordinator removePersistentStore:store error:&error])
    {
        CLS_LOG(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    // Delete file
    if ([[NSFileManager defaultManager] fileExistsAtPath:store.URL.path]) {
        if (![[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:&error])
        {
            CLS_LOG(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }

    // Delete the reference to non-existing store
    [persistentStoreCoordinator release];
    persistentStoreCoordinator = nil;
    NSPersistentStoreCoordinator *r = [self persistentStoreCoordinator];

    return r;
}

My app has a single store, so I don't think NSPersistentStore *store = [[persistentStoreCoordinator persistentStores] lastObject]; would cause an issue.

Ned
  • 6,280
  • 2
  • 30
  • 34
  • 1
    This sounds like a synchronization problem. Is it possible the store is being accessed on more than one thread at a time? – Steven McGrath May 11 '13 at 05:11
  • Did you happen to change core data model in your recent app update? If yes you need to version your core data model. [Refer Docs](http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/CoreDataVersioning/Articles/Introduction.html), [Tutorial](http://mobile.tutsplus.com/tutorials/iphone/core-data_schema-versioning-and-lightweight-migrations/) – Amar May 14 '13 at 10:24
  • I did update my model, adding a new version to my .xcdatamodeld file. I only added attributes so lightweight migration occurred. I am not accessing the store on multiple threads. – Ned May 14 '13 at 17:24
  • Ned, have you taken your Core Data model into a static framework or sub-project? I have a vague recollection of this occurring when a new app version has refactored database code into a sub-project and the user with an existing signed install (e.g. already has the datastore created within the app sandbox) and upgrades to the new version. I think something to do with how you deal with the (mom) file during the build process. Sorry I can't be more specific as I can't remember the details now, but this might provide a pointer. – TheBasicMind May 16 '13 at 07:41
  • If I understand correctly this happens on user's devices but not on every one? Have you been able to acquire a "broken" sqlite file to examine it? If you do - compare the model with one that has successfully migrated, check the schema is the same and that you can perform queries. Also - does the error occur only on write operations (saveContext) or reads too? – Dimitar K May 17 '13 at 12:34
  • From your comment below: "I do delete the entire store at certain times (rebuilding it later).". You *are* releasing the old PSC and MOC and initializing new ones for the new store, right? – Danra May 17 '13 at 16:49
  • @DimitarK: I haven't been able to get a broken sqlite file. Not sure if I'll be able to, but if I do I will inspect it as you said. The error only occurs on write operations. – Ned May 17 '13 at 17:28
  • @Danra: I am using the reset method from this SO answer: http://stackoverflow.com/a/4436522/373370 . My app only has a single persistent store, so I don't think the FIXME line would be causing me to delete the wrong store. – Ned May 17 '13 at 17:33
  • Please share all the relevant Core Data code in your app. – Danra May 17 '13 at 19:45
  • @Danra Question has been edited to add relevant code. – Ned May 17 '13 at 20:02
  • 2
    Any chance you set your app to keep running in the background? In that case the store won't be available since it's encrypted. – Danra May 17 '13 at 20:23
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/30156/discussion-between-ned-and-danra) – Ned May 17 '13 at 21:56
  • A little late to respond, but I noticed that this error is almost always (98% of the time) when the app has been pushed to the background. – Daniel Jun 24 '18 at 19:37

2 Answers2

1

A little late to respond, but I noticed that this error almost always occurred when our app had been pushed to the background.

When creating the persistentStoreCoordinator you may need to set the NSPersistentStoreFileProtectionKey option to NSFileProtectionCompleteUntilFirstUserAuthentication instead of NSFileProtectionComplete.

Note that this slightly elevates the security risk so you may want to consider if this is necessary in your app.

Daniel
  • 8,794
  • 4
  • 48
  • 71
0

Are you sure that the database is correctly opened / closed at everytime? It can be a problem due to a file open while it was uncorrectly closed

Apollo
  • 196
  • 1
  • 9
  • You don't have to explicitly close the CoreData store after performing a fetch, do you? Not sure how this would be happening. I do delete the entire store at certain times (rebuilding it later). Maybe it could have to do with this? – Ned May 14 '13 at 17:27
  • @Apollo this isn't an answer. This belongs in the comments section. –  May 17 '13 at 15:03