Network extension authorization dialog not appearing

This has happened a few times, including out in the field; it's happened on macOS 14 and 15 I think.

"This" is: our app runs, activates the extension, it has to get user approval, and... the system dialogue window never appears. The extension stays waiting for user approval. I've got sysdiagnose from one of the systems, and I see the system log about it going into the user approval needed state, and... nothing else.

It's there in Settings, and can be approved then.

Has anyone run into this? Ever?

Answered by DTS Engineer in 845962022

I just filed FB17948001, with the sysdiagnose attached.

So, I was able to take a look at it today and there are definitely some oddities I don't understand. FYI, the list of things below constructed across a significant period of time, so it isn't order in any particular way nor is it necessarily "prioritized".

(a) One of your components ("ProxyAgent") crashed twice, with a reboot in between. You can find the crash log data in the system log archive by looking for pids "1401" and "339". The crashes themselves are fairly similar, with a high thread ID GCD thread crashing here:

6  <redact>	       0x101581398    -[ExtensionLoader asyncControlProxyWithErrorHandler:]
7  <redact>	       0x10102cebd    -[AppBypassCollector sendAppEntry:]
8  <redact>	       0x10157e847    -[AppEntryCollector addName:]
9  <redact>	       0x10102d2a2    AppBypassConfig::sendListByOS(std::__1::set<std::__1::basic_string<char, 
10 <redact>	       0x1010226d5    AppBypassConfig::sendAppBypassList()

I'm not sure how it connect (if at all), but the timing is suspicious as it lines up with about the time the system extension activation started.

(b) An app update is what triggered this (from 1.0.16593-> 1.0.16594) and one of my questions is whether or not the extension actually updates. More specifically, there are two components I'm concerned about here:

  1. The Info.plist data, particularly the version number.

  2. The build UUID

Across most of the system (particularly, the "high" level system), the Info.plist data (particularly the bundle ID and version data) are how the system references and manages "your app". However, some lower level components actually use the build UUID as their unique identifier.

My concern here is how this interacts with codesigning, as ANY change to the bundle contents will change the bundle ID, but only build changes will modify the build UUID. Depending on your build strategy, that means you can end up in a situation where the build UUID hasn't changed (because the executable didn't change) but the bundle data HAS (because, for example, you increased the build number).

I don't know if this applies in your case, if you are incrementing version numbers with every release (even when a specific executable didn't change), then I would recommend modifying your code to ensure that it DOES change.

(c) I'm not sure that it will actually effect anything, but trustd does not like the entitlement configuration of your app group and logs a ton of this message:

"...is ignored because of invalid application signature or incorrect provisioning profile"

(d) There are multiple EndpointSecurity clients active, one of which appears to be semi-broken based on this message (~4000):

Failed to open service: 0xe00002d8: Caller lacks TCC authorization for Full Disk Access

Note that EndpointSecurity API is particularly dangerous because it's basically capable of silently disrupting anything in the entire system.

(e) This failure caught my eye:

2025-06-09 15:16:48.915439+0530 sysextd: [com.apple.sx:Staging] 
Removing tree at
  path: /Library/SystemExtensions/30D6C688-7BDF-46EE-A7AF-459AB7699380
failed with
  error: “30D6C688-7BDF-46EE-A7AF-459AB7699380” couldn’t be removed.
Resetting flags and trying again!

Are you setting UF_IMMUTABLE on anything in your extension? It was fixed before the system your on (23B45 vs 23F79), but there was a bug (r.113410811) in sysextd which would prevent staging from completing if the UF_IMMUTABLE bit had been set on any component.

(f) As far as I can tell, the direct reason we never presented the dialog is that, as far as the sysextd was concerned, we already had:

2025-06-09 15:32:13.834324+0530 sysextd: activateDecision found existing entry of same version: state activated_waiting_for_user, ID 30D6C688-7BDF-46EE-A7AF-459AB7699380
2025-06-09 15:32:13.834345+0530 sysextd: initial activation decision: coalesceWithActivationInProgress(<sysextd.Extension: 0x7fc262405b60>)

It's likely something else occurred during the first activation pass which prevented the initial notification, however, I didn't see any explanation in the log data and, unfortunately, the first activation occurred before the most recent power cycle (which purges significant log data).

...and that's what I've found.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

"This" is: our app runs, activates the extension, it has to get user approval, and... the system dialogue window never appears. The extension stays waiting for user approval. I've got sysdiagnose from one of the systems, and I see the system log about it going into the user approval needed state, and... nothing else.

It's there in Settings, and can be approved then.

Has anyone run into this? Ever?

I'm not sure I've seen the EXACT case you're describing, but I've certainly seen similar kinds of failures/glitches. In general, they loosely fall into three broad categories:

  1. External factors. In practice, macOS is sufficiently complicated and flexible that there is nearly always "something" that can change or disrupt the normal system logic. In most cases this involves some kind of 3rd party app/component, though can sometimes be internal to the system itself.

  2. Development "weirdness". The nature of software development means that developers routinely do things that are WELL outside what "normal" system usage looks like. For example, very few users have dozens or hundreds of copies of the "same app" laying around on their system (collections of older builds), nor does any user routinely delete/update/install the same app dozens of times per day (the build/run/build cycle).

  3. System bugs. This is generally the least common case but, yes, the system does have bugs and things do break.

Note that #1 in particular is the most critical are to watch for. It's both the one your most likely to be able to actually fix and, more importantly, it's more likely to hurt your product in a "systemic" way. Putting that point in more concrete terms, an issue that only effects 100 users is a much bigger problem when those are the 100 users you want to sell your app to.

Moving to the specific issue:

"This" is: our app runs, activates the extension, it has to get user approval, and... the system dialogue window never appears. The extension stays waiting for user approval.

What extension point are you trying to activate and what's the market/environment you're selling to/seeing issues with? If you're targeting the business/enterprise market, one component I would watch for are EndpointSecurity apps/extensions. They're much more common on enterprise machines and, by design, they basically have an unbounded ability to disrupt the system's "normal" operation.

I've got sysdiagnose from one of the systems, and I see the system log about it going into the user approval needed state, and... nothing else.

Since you have a sysdiagnose, if you file a bug on this and then post the bug number back here, I will try and take a look at the log (as well as ensuring it get to the right team). I can't promise I'll find anything, but I've stared at enough of them to have gotten reasonably good at finding clues that were otherwise hidden.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

The extension is a Transparent Proxy Provider, so that's the authorization that should come up. We've had this happen occasionally on our automated tests, which run in a VM with no other extensions installed, so I think I can rule that out. (The automated software expects the window to appear, so it can then "click" it; it doesn't, so the test fails. Intermittently.)

I just filed FB17948001, with the sysdiagnose attached.

I know it's WWDC so honestly I'm not expecting anyone to have a lot of spare cycles. 😄

I just filed FB17948001, with the sysdiagnose attached.

So, I was able to take a look at it today and there are definitely some oddities I don't understand. FYI, the list of things below constructed across a significant period of time, so it isn't order in any particular way nor is it necessarily "prioritized".

(a) One of your components ("ProxyAgent") crashed twice, with a reboot in between. You can find the crash log data in the system log archive by looking for pids "1401" and "339". The crashes themselves are fairly similar, with a high thread ID GCD thread crashing here:

6  <redact>	       0x101581398    -[ExtensionLoader asyncControlProxyWithErrorHandler:]
7  <redact>	       0x10102cebd    -[AppBypassCollector sendAppEntry:]
8  <redact>	       0x10157e847    -[AppEntryCollector addName:]
9  <redact>	       0x10102d2a2    AppBypassConfig::sendListByOS(std::__1::set<std::__1::basic_string<char, 
10 <redact>	       0x1010226d5    AppBypassConfig::sendAppBypassList()

I'm not sure how it connect (if at all), but the timing is suspicious as it lines up with about the time the system extension activation started.

(b) An app update is what triggered this (from 1.0.16593-> 1.0.16594) and one of my questions is whether or not the extension actually updates. More specifically, there are two components I'm concerned about here:

  1. The Info.plist data, particularly the version number.

  2. The build UUID

Across most of the system (particularly, the "high" level system), the Info.plist data (particularly the bundle ID and version data) are how the system references and manages "your app". However, some lower level components actually use the build UUID as their unique identifier.

My concern here is how this interacts with codesigning, as ANY change to the bundle contents will change the bundle ID, but only build changes will modify the build UUID. Depending on your build strategy, that means you can end up in a situation where the build UUID hasn't changed (because the executable didn't change) but the bundle data HAS (because, for example, you increased the build number).

I don't know if this applies in your case, if you are incrementing version numbers with every release (even when a specific executable didn't change), then I would recommend modifying your code to ensure that it DOES change.

(c) I'm not sure that it will actually effect anything, but trustd does not like the entitlement configuration of your app group and logs a ton of this message:

"...is ignored because of invalid application signature or incorrect provisioning profile"

(d) There are multiple EndpointSecurity clients active, one of which appears to be semi-broken based on this message (~4000):

Failed to open service: 0xe00002d8: Caller lacks TCC authorization for Full Disk Access

Note that EndpointSecurity API is particularly dangerous because it's basically capable of silently disrupting anything in the entire system.

(e) This failure caught my eye:

2025-06-09 15:16:48.915439+0530 sysextd: [com.apple.sx:Staging] 
Removing tree at
  path: /Library/SystemExtensions/30D6C688-7BDF-46EE-A7AF-459AB7699380
failed with
  error: “30D6C688-7BDF-46EE-A7AF-459AB7699380” couldn’t be removed.
Resetting flags and trying again!

Are you setting UF_IMMUTABLE on anything in your extension? It was fixed before the system your on (23B45 vs 23F79), but there was a bug (r.113410811) in sysextd which would prevent staging from completing if the UF_IMMUTABLE bit had been set on any component.

(f) As far as I can tell, the direct reason we never presented the dialog is that, as far as the sysextd was concerned, we already had:

2025-06-09 15:32:13.834324+0530 sysextd: activateDecision found existing entry of same version: state activated_waiting_for_user, ID 30D6C688-7BDF-46EE-A7AF-459AB7699380
2025-06-09 15:32:13.834345+0530 sysextd: initial activation decision: coalesceWithActivationInProgress(<sysextd.Extension: 0x7fc262405b60>)

It's likely something else occurred during the first activation pass which prevented the initial notification, however, I didn't see any explanation in the log data and, unfortunately, the first activation occurred before the most recent power cycle (which purges significant log data).

...and that's what I've found.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

I am still digesting that, but I was about to upload another sysdiagnose -- this one from a githubs action VM that demonstrated the same behaviour (but which was a clean install of our app). But I think I'll try to fix some of the obvious-fixable issues there.

We don't have UF_IMMUTABLE set on anything, and the one process in the suite that uses ESF doesn't protect anything in /Library/SystemExtensions. That process needs the TCC, but without MDM, it requires manual intervention by the user. I don't think it does it on the github actions tests.

Each build gets a new number; for annoying reasons, the build is done twice (Apple Silicon and Intel), lipo'd together, and then codesigned again.

The crashes you note are either segfaults or reference count crashes, and should not happen -- it seems to be an issue with XPC. The code in question is written in ObjC.

I am still digesting that, but I was about to upload another sysdiagnose -- this one from a githubs action VM that demonstrated the same behaviour (but which was a clean install of our app).

There was a sysdiagnose from macOS 13.7.6 uploaded which I did look over. Unfortunately, that appears to be a different issue, as sysextd is actually crashing before before it starts authorizing. This does appear to be a known issue (r.99777199), however, there haven't been really been reports post-macOS 13. If you're seeing this crash on more recent releases then that's worth further investigations/bugs, but I don't think there's a lot to be done on macOS 13.

Each build gets a new number; for annoying reasons, the build is done twice (Apple Silicon and Intel), lipo'd together, and then codesigned again.

For what it's worth, I don't actually have any problem with incrementing all component versions, even when a give component doesn't change. Given the possible complexity of component interactions, it's entirely possible for an identical component to crash/fail/behave differently across versions (because of how other, changed, components behave) and keeping the version numbers in sync with each other is an easy way to track that. HOWEVER, if you're going to do this, then I think it's also important that you ensure the build UUID changes with every release. As I mentioned above, there are part of the system that use that to track/identify executables and I think it's worth avoiding the situation where some of the system sees your component as "new" (because it's plist version and codesign configuration changed) while other parts of the system don't (because it's slice UUID didn't change).

I linked to TN3178 yesterday and it references two ways to ensure this:

  1. The "-random_uuid" linker flag, which generates a random UUID for every build.

  2. The end recommendation "If that doesn’t resolve the issue, add some unique code to each app".

Both of those will work fine, but the main issue with "-random_uuid" is that it's going to extend your build time, since it will end up triggering work that wouldn't otherwise be necessary. I haven't worked out the details of this, but I think there's preprocessor symbol (CURRENT_PROJECT_VERSION) that will give you access to the same version number that Xcode embeds in the Info.plist. I'd probably log that define to unique the build.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

This is still happening (or seeming to happen) on macOS 14.1 and 15.3.1, sigh

(Oops, forgot to mention: one fun case we seem to have is: if the dialogue does not appear, but the system thinks it has, then it will never show up until the system is rebooted. An invisible dialogue box.)

Network extension authorization dialog not appearing
 
 
Q