URLSession download task fails with NSPOSIXErrorDomain error Code=1

I have a Push Notification Service Extension, which is processing notification payload to attach image, if imageUrl is key is present.

I use this simple code to perform the download:

let downloadTask = URLSession.shared.downloadTask(with: urlRequest) {
	[weak self] tempURL, response, error in
	
	/// parse results...
}

Notification payload contains "mutable-content" : 1 inside aps. It's entirely randomly will it work or not.

When it doesn't work, I get this error:

	Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted" UserInfo={NSErrorFailingURLStringKey=https://w7.pngwing.com/pngs/1005/607/png-transparent-african-elephant-animal-elephant-thumbnail.png, NSErrorFailingURLKey=https://w7.pngwing.com/pngs/1005/607/png-transparent-african-elephant-animal-elephant-thumbnail.png, _NSURLErrorRelatedURLSessionTaskErrorKey=(
	    "LocalDownloadTask <4A847242-2314-4125-99E4-A424CF4B4B7C>.<7>"
	), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDownloadTask <4A847242-2314-4125-99E4-A424CF4B4B7C>.<7>}

I have no idea what Apple's internal code throws this error, what could possibly go wrong here.

This is happening for a while now, I just re-tested on iOS 17.5.1 on 14 Pro. App is compiled using Xcode 15.4 and Swift 5.10, latest SDK.

Not sure is it relevant but main iOS app has DataProtection capability set to Complete.

It's entirely randomly will it work or not.

I’d like to clarify what you mean by this:

  • Is it that this consistently fails on some devices and consistently works on others?

  • Or that it works for some invocations of your extension but fails for others?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

What I meant: on the same iPhone, I receive say 10 push notifications sent from our backend (which goes to Firebase, which sends notification through APNs), all with same structure with only difference being imageUrl.

It's entirely random how many will have download task succeed and attach downloaded image file to the UNMutableNotificationContent and how many downloads will fail with the said error.

Sometimes most succeed. Sometimes none. Often 2-3 are OK, the rest fail. I checked SSL, TLS, whatever I could think for the web servers where the files are located, that was all fine.

Today I fired up the debugger session for the PNSE process and re-run the notification that has just failed minutes ago only to see the download returns with URL and no error. When I do get the error in the debugger session, I don't see anything useful in the stack frame that could tell me where is the problem.

I also tried using URLSesssion instance with custom configuration, seem to behave the same.

extension URLSessionConfiguration {
	static var preferred: URLSessionConfiguration {
		let c = URLSessionConfiguration.default
		c.allowsCellularAccess = true
		c.httpCookieAcceptPolicy = .never
		c.httpShouldSetCookies = false
		c.httpAdditionalHeaders = [
			"Accept-Charset": "utf-8",
			"Accept-Encoding": "gzip, deflate",
		]
		c.requestCachePolicy = .returnCacheDataElseLoad
		return c
	}
}

No idea what else to try...

(I tried to attach the .swift file contents here using either Add text or Add file), but the form won't let me do that.)

OK. But is this just on that specific iPhone? Or do you see this pattern of behaviour on a wide range of devices?


(I tried to attach the .swift file contents here using either Add text or Add file), but the form won't let me do that.)

Right. If you change the extension to .txt, you’ll be able to attach that. OTOH, it’s probably not necessary for me to look at your code right now.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Yes, it happens on many iPhones. This is issue in live app thus we see it on quite a variety of devices.

Here is debugger state, as detailed as I can get it, for the case when this error happens.

URLResponse is HTTP 200 so the picture is downloaded OK.

But tempURL (first param in downloadTask's closure) is nil so this is some error in local file handling in code that prepares downloaded file for handing over to my process.

This is interesting. I suspect it’s something to do with data protection but it’s hard to be sure based on the info we currently have. My suspicion is based on the fact that the error, Posix error 1, aka EPERM, is most commonly associated with file system access being blocked by the sandbox [1].

Unfortunately the error state you’ve captured doesn’t show any more detail about this, and specifically the file URL that’s triggering it (assuming my suspicions are correct).

There are a couple of ways you might investigate this:

  • Switch from the convenience API to the delegate based API. This gives you more insight into what’s actually happening within the download. I suspect you’ll find that the urlSession(_:downloadTask:didFinishDownloadingTo:) method is not being called but the urlSession(_:task:didCompleteWithError:) method is, but your test would confirm that.

  • Look in the system log. In many cases problems like this result in tell-tale signs in the system log. My advice here is that you add a log point just before you start the download and a log point when you see this failure, then snapshot the log when it fails, and use that snapshot to look at the log entries between your two log points. Your Friend the System Log has more on this idea.

Do NSEs work on the simulator? If so, do you see this on the simulator?

Not sure is it relevant but main iOS app has DataProtection capability set to Complete.

What is that set to for your NSE appex?

IMPORTANT Don’t rely on what Xcode says. Instead, dump the entitlements on your appex to see how it’s actually signed. So, do this:

% codesign -d --entitlements - /path/to/your.app/PlugIns/your.appex

and then look for what com.apple.developer.default-data-protection is set to (if anything).

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] My On File System Permissions post covers this, albeit from a very Mac-centric perspective.

Entitlements path was set on Project level thus both iOS app and PNSExt used the same file.

I split this to be set per target and just removed DataProtection capability from the entitlements used by PNSE. Initial test on several devices seems to indicate that problem went away. We will deploy updated build to customers and monitor what's happening.

Did someone forgot to use startAccessingSecurityScopedResource() for this? 🙃

(Thanks for timely answers!)

Glad to hear you’re making progress.

One thing to watch out for here is that, once the data protection is set on your container, it can be hard to reset (because of the way that items inherit protection when they’re created). Xcode can mask this because Xcode installs apps in a weird way. If you get to the TestFlight stage and still see problems, try deleting the app and installing it from scratch.

Note that this might not be the ultimate solution for your users, but it’s a useful diagnostic data point.

Did someone forgot to use startAccessingSecurityScopedResource() for this?

I wish it were that easy. Data protection is cryptographically enforced, so you can’t access protected data with a simple API call. The user has to provide the credentials to unlock the data.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

URLSession download task fails with NSPOSIXErrorDomain error Code=1
 
 
Q