Core Data "The Database appears corrupt" -- What causes this error? Core Data "The Database appears corrupt" -- What causes this error? sqlite sqlite

Core Data "The Database appears corrupt" -- What causes this error?


Okay, I've tracked it down. It appears that there is something broken in propertiesToFetch against NSManagedObject resultType (not supposed to be used, our mistake) in this setup -- against a context that has a parent context instead of a persistent coordinator. This unit test shows that all you have to do is set a property to fetch to get this error (while doing the query without the properties to fetch works correctly). The fix here for us was to stop incorrectly using properties to fetch :)

- (void)testManagedObjectContextDefect{            NSManagedObjectContext *contextA = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];    contextA.persistentStoreCoordinator = sqllitePersistentStoreCoordinator;    NSManagedObjectContext *contextB = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];    contextB.parentContext = contextA;    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"GCSCObject" inManagedObjectContext:contextB];    [contextB performBlockAndWait:^{        GCSCObject *object = [[GCSCObject alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:contextB];        object.serverID = @"1";        NSError *error = nil;        XCTAssert([contextB save:&error] && !error, @"Failed to save - %@",error); // B -> A save    }];    [contextA performBlock:^{        NSError *error = nil;        XCTAssert([contextA save:&error] && !error, @"Failed to save - %@",error); // A -> PSC, background save    }];    [contextB performBlockAndWait:^{        NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"GCSCObject"];        NSError *error = nil;        NSArray *results = [contextB executeFetchRequest:request error:&error];        XCTAssert(results.count == 1 && !error, @"Fetch failed to retrieve - %@ / %@",results,error);        GCSCObject *object = results[0];        XCTAssert([object.serverID isEqualToString:@"1"], @"Value retrieval failed");        // Everything passes up to here, so far so good!        request = [[NSFetchRequest alloc] initWithEntityName:@"GCSCObject"];        request.propertiesToFetch = @[@"serverID"]; // This is the culprit of the index crash        results = [contextB executeFetchRequest:request error:&error];        XCTAssert(!error, @"%@", error.localizedDescription); // !!! HERE we have a failure, assert: "Core Data: error: -executeRequest: encountered exception = The database appears corrupt.  (invalid primary key) with userInfo = { NSFilePath = "/path/db.sqlite }";    }];}

In this case GCSCObject is a regular entity, and serverID is one of it's parameters (it doesn't matter which parameter is used, or what type it is, I tried with multiple. Here is the description of the serverID parameter I used for this test:

serverID description

The crash occurs wether or not we provide the andWait to contextA save (though doing so would kind of void the point of having a background queue for saving)

I'd love feedback regarding why this might be the case, but for now, not using properties to fetch allows our application to work smoothly. I'm contemplating filing an Apple Bug here.


If you experience this error when performing a fetch request that is retrieving some aggregate result (sum, max, min, ...) make sure you set

fetchRequest.resultType = NSDictionaryResultType;


First, your UI context should not be a private queue context. That is what the NSMainQueueConcurrencyType is for.

Second, do not check the error on save. Check the BOOL return from the -save:. That error can have junk in it even on a successful save.

Third, what do your other two saves look like? If they are all on private queues and being saved async then you may be running into a race condition. C should save synchronously, B should save synchronously and then A should be async.