NSURLSession background upload completes with NSPOSIXErrorDomain Code=1 when data protection is set to completeUnlessOpen

Our app is using background NSURLSession to upload images. The app has data protection set to NSFileProtectionCompleteUnlessOpen.

If the device is locked when an upload completes, the didCompleteWithError is executed with the following error(I've removed the NSErrorFailingURLStringKey and NSErrorFailingURLKey.):



Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted" UserInfo={_NSURLErrorRelatedURLSessionTaskErrorKey=(
    "BackgroundUploadTask <4476D3BD-541B-4050-BFC6-0B8C6A174FE9>.<20>",
    "LocalDownloadTask <4476D3BD-541B-4050-BFC6-0B8C6A174FE9>.<20>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundUploadTask <4476D3BD-541B-4050-BFC6-0B8C6A174FE9>.<20>}.


Although the reqeust finishes with the above error, the file is uploaded successfuly, the request's status code is 200 and the image is saved on the server side.


We looked at the device console log and just before the didCompleteWithError is called we saw this line from nsurlsessiond (I've replaced the bundle id with <bundle id>)


nsurlsessiond Couldn't set data protection class of /private/var/mobile/Containers/Data/Application/BA74D3B0-8AA4-4E0B-9BE0-011B36B9A7F0/Library/Caches/com.apple.nsurlsessiond/Downloads//CFNetworkDownload_7gxBlo.tmp, error: Error Domain=NSCocoaErrorDomain Code=257 "The file “CFNetworkDownload_7gxBlo.tmp” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/private/var/mobile/Containers/Data/Application/BA74D3B0-8AA4-4E0B-9BE0-011B36B9A7F0/Library/Caches/com.apple.nsurlsessiond/Downloads//CFNetworkDownload_7gxBlo.tmp, NSUnderlyingError=0x10343a300 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}


It looks like the nsurlsessiond is trying to modify the data protection class or read it and fails.

We tested and changed the Caches directory data protection to NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication and the error didn't happen again.

Based on that we concluded NSFileProtectionCompleteUnlessOpen is the culprit.

How can we fix the failing request with the above error and still have data protection NSFileProtectionCompleteUnlessOpen?

A few things could be happening here depending on your situation; First, if you are only uploading a local file then before your file is uploaded it is written to a temporary file and then uploaded. Upon successful upload the temporary file is then deleted and it is failing because of NSFileProtectionCompleteUnlessOpen. Or, the other case would be that you are downloading a file first and then uploading it, in which case the same situation applies. I believe this to be working as expected, your temporary file is just not deleted. Files in temporary directories will be cleaned up by the operating system at some point.



Matt Eaton

DTS Engineering, CoreOS

meaton3 at apple.com

Thanks for the prompt reply.


First case is our use case. We are only uploading local images with uploadTask(with request: URLRequest, fromFile fileURL: URL).

Do you suggest we then treat the task as successful when the request is successful and the error is NSPOSIXErrorDomain Code=1?

Since you are receiving a 200 response from your server and you can verify that the file was saved then I would say, yes, that this is an indication that the actual network request was successful. I would recommend that you work out the NSPOSIXErrorDomain error based on what makes sense for your application. For files that need to be uploaded or transfered, can you mark these files individually with the NSFileProtectionKey of NSFileProtectionCompleteUntilFirstUserAuthentication?



Matt Eaton

DTS Engineering, CoreOS

meaton3 at apple.com

Thank you for the feedback.

We strive to have the highest protection possible, that's why we can't lower the protection level.

Based on our tests, the files are not copied to our app's Caches directory. If the system would copy the files there, they wouldn't even be accessible with the protection NSFileProtectionCompleteUnlessOpen. The .tmp file is created only after the upload finishes. Usually, it's empty or 32 bytes in size.

I did some more digging on this to try and see what could be getting written that is 32 bytes in size. What could be happening is that if your application process is not alive during the background transfer then the response from the server needs to be written somewhere when your process is not alive and this may be what you are seeing.


To dig into this further it would be best to open a TSI and include a sample project and sysdiagnose of the issue to provide more information.


Matt Eaton

DTS Engineering, CoreOS

meaton3 at apple.com

Thank you.

NSURLSession background upload completes with NSPOSIXErrorDomain Code=1 when data protection is set to completeUnlessOpen
 
 
Q