Gatekeeper rejects notarized app ("Unnotarized Developer ID") when using necessary entitlements

Hello everyone,

I'm hoping to get some guidance on a frustrating codesigning issue. I have a macOS application that successfully completes the entire notarization and stapling process, but it is still rejected by Gatekeeper during the final verification step. The rejection only happens when I apply the entitlements that I believe are necessary for my app's functionality.

The application is built with PyInstaller and has the following components:

  • A main executable written in Python.
  • A bundled Tcl/Tk instance for the GUI.
  • Embedded Playwright components, which include the Node.js runtime and a full Chromium browser instance. These are located deep inside the .app bundle.

The Problem

The core of my application relies on Playwright to perform some automated tasks, and its bundled Chromium browser requires specific entitlements to function under the Hardened Runtime. Specifically, it needs com.apple.security.cs.allow-jit and com.apple.security.cs.allow-unsigned-executable-memory.

My signing process is as follows:

  1. Prepare Entitlements: I use two separate .plist files:
  • main_app_entitlements.plist: This is for the main Python executable and only contains com.apple.security.cs.allow-jit.
  • jit_helper_entitlements.plist: This is for the node and Chromium Helper executables within the Playwright framework. It contains both com.apple.security.cs.allow-jit and com.apple.security.cs.allow-unsigned-executable-memory.
  1. Inside-Out Signing: I perform a deep signing process. I find all binaries, dylibs, and frameworks, sort them by path length (deepest first), and sign each one individually with the appropriate entitlements. The main .app bundle is signed last.
  2. Notarization: I zip the .app bundle and submit it using xcrun notarytool submit --wait. The tool reports a successful notarization every time.
  3. Stapling: I use xcrun stapler staple on the .app bundle, and it confirms that the ticket was successfully stapled.

The point of failure

The final step is to verify the result with spctl: spctl --assess --type execute --verbose --ignore-cache "MyApp.app" This is where it fails.

The output is: MyApp.app: rejected source=Unnotarized Developer ID This "Unnotarized Developer ID" message is confusing because xcrun notarytool and stapler both report complete success.

The crucial detail

If I run the entire process without any entitlements—just signing with the Hardened Runtime enabled—the final spctl assessment passes. However, the application then crashes at runtime as soon as it tries to use Playwright, which is expected since the browser helpers are missing their required JIT entitlements.

My question

Is there a known issue where using com.apple.security.cs.allow-jit or com.apple.security.cs.allow-unsigned-executable-memory on nested helper executables can invalidate an otherwise successful notarization?

Is my strategy of applying different, granular entitlements to different executables within the same app bundle correct?

Could the issue be related to how or when these entitlements are applied during an "inside-out" signing process? Is there a better way to structure the signing of these complex components?

I'm confident the notarization itself is working, but it seems Gatekeeper's local assessment is stricter and is being tripped up by my entitlement configuration.

Thank you in advance for any help or suggestions you can provide

Answered by DTS Engineer in 851144022
1. I find it completely illogical that gatekeeper rejects notarised apps.

You’re not the first person to make that observation. If you want to get that feedback to the folks who have the power to enact change, I recommend that you file a bug about it.

Please post your bug number, just for the record.

2. If the errors appear in the system log, why aren't they reported also in the warning message of gatekeeper?

Because Gatekeeper is targeted at users, not developers, and that information would be completely useless to the vast majority of users.

3. syspolicy_check was useless in my case since it didn't provide any extra info that helped me to debug.

Indeed. syspolicy_check is the right tool for investigating problems like this. We’re using your bug (FB19042844) to track improving it to debug this case.

Share and Enjoy

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

You listed various hardened runtime exception entitlements. That list didn’t include com.apple.security.cs.disable-library-validation, which is weird because none of those other entitlements affect Gatekeeper but the library validation one does. For the details, see Resolving Gatekeeper Problems Caused by Dangling Load Command Paths.

So, are you sure you haven’t included com.apple.security.cs.disable-library-validation in your entitlements.

The final step is to verify the result with spct

spctl is not a tool I put much faith in. Rather, I recommend the approach in Testing a Notarised Product.

Share and Enjoy

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

No, I haven't added that. Is it possible that maybe this library entitlement is added automatically during codesigning?

Actually after further testing, I realized that the culprit seem to be the entitlements I assign to the node and Chromium Helper executables within the Playwright framework ( com.apple.security.cs.allow-jit and com.apple.security.cs.allow-unsigned-executable-memory ).

The JIT entitlement applied to the main python executable does not affect gatekeeper.

I used syspolicy_check and this is the message I got:

App has failed one or more pre-distribution checks.

Codesign Error
    File: HotelOrganizer.app 
    Severity: Fatal 
    Full Error: Gatekeeper rejected this file. If there isn't a more 
        descriptive error elsewhere in this output, please file a Feedback 
        through Feedback Assistant.app so we can continue to improve 
        syspolicy_check. Please include the app bundle you are checking and a 
        sysdiagnose taken immediately after running syspolicy_check. 
    Type: Notary Error 

I'm really frustrated by this, I tried everything I could find in the forum. I cannot distribute my app to my customers because of this issue.

Hey hey, at least that error is honest (-:

At this point I recommend that you do what that message says, that is:

  • File a bug against the syspolicy_check tool.
  • Include a sysdiagnose log taken immediately after running the tool.
  • Also include a copy of the app you’re checking.

I’ve been working with the maintainers of syspolicy_check to improve it’s ability to diagnose problems like this, and your bug will help with that process.

Once you’re done, please post the bug number here. I’ll then get your app from the bug and rummage around myself. Usually I can puzzle this stuff out.

Share and Enjoy

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

Every good debugging story starts with a “Huh, that’s weird.”, and this is no exception (-:

Consider this:

% stapler validate -v HotelOrganizer.app 
…
Downloaded ticket has been stored at file:///var/folders/n_/p9vcphfj2l7c7fmh0ct2f70w0000gp/T/4985875e-0770-4d79-8ec1-14c034783d98.ticket.
The validate action worked!

So far so good. But now look at this:

% NotarizationTicketDump /var/folders/n_/p9vcphfj2l7c7fmh0ct2f70w0000gp/T/4985875e-0770-4d79-8ec1-14c034783d98.ticket 
b4563a07ac6827cced5dd13a172c41c80ca7d589

Note NotarizationTicketDump is a tool I wrote myself to dump the cdhashes in a ticket. I can’t share that tool but you, as the person who did the notarisation, can get the same information from the notarisation log. More on this below.

The ticket has only one cdhash value. That value matches your main app:

% codesign -d -vvv HotelOrganizer.app 
…
CDHash=b4563a07ac6827cced5dd13a172c41c80ca7d589
…

which is good, but your app contains a lot of other Mach-O images [1]:

% FindMachO.sh HotelOrganizer.app | wc -l 
     157

and none of them are in the ticket. So…

Huh, that’s weird.


Looking in the log there’s clear evidence that this is the cause of your Gatekeeper problem. Let’s pick one candidate Mach-O within your app:

% codesign -d -vvv HotelOrganizer.app/Contents/Frameworks/libtcl8.6.dylib
…
CDHash=b9ad061ca5c5fc4c8c61ad59b9f0ca110e856eba
…

I chose this one because it’s in the right place, that is, it follows the rules in Placing Content in a Bundle [2]. The notary service should’ve been able to find this and include it in your ticket.

However, there’s clear evidence that’s not the case. For a start, it’s not showing up in the ticket. But we also see evidence from Gatekeeper itself. Consider this pair of log entries:

type: info
time: 2025-07-25 03:42:14.873464 -0700
process: syspolicyd
subsystem: com.apple.syspolicy
category: default
message: looking up ticket: {… 0xb9ad061ca5c5fc4c8c61ad59b9f0ca110e856eba}, 2, 0

type: info
time: 2025-07-25 03:42:14.873517 -0700
process: syspolicyd
subsystem: com.apple.syspolicy
category: default
message: completing lookup: {… 0xb9ad061ca5c5fc4c8c61ad59b9f0ca110e856eba}, 3

Note To see the full log entries I had to enable private data. I have a link to a post that explains that in Your Friend the System Log.

In the second log entry, the 3 corresponds to ESRCH, that is, Gatekeeper wasn’t able to find a ticket for this cdhash.


So what’s going on? Honestly, I’ve no idea. It seems like the notary service issued a ticket for your app that doesn’t include all the Mach-O images in your app. That’s weird. Normally a notary problem will result in an error, not a partial ticket.

To learn more I need to see a copy of your notary log. For info on how to get that, see Fetching the Notary Log. Please download the log, attach it to your bug, and then write back here letting me know.

Note that you can review your recent notarisation history using notarytool history, so if you’ve forgotten the request UUID you can recovery it from there.

IMPORTANT Make sure the log corresponds to the app on the disk image that you attached to your bug. An easy to way to confirm that is to look in the log for the cdhash of the app, that is, b4563a07ac6827cced5dd13a172c41c80ca7d589.

Finally, as part of my investigation I tried notarising your app myself. That worked, and the resulting notary log shows cdhashes for all your Mach-O images. Moreover, once I did that your app started to pass Gatekeeper. So it’s important for me to see the notary log of your original submission.

ps It’s better to reply as a reply, rather than in the comments; see Quinn’s Top Ten DevForums Tips for this and other titbits.

pps I noticed that you’re notarising a zip archive and you create that archive with --sequesterRsrc. Don’t do that. Ideally your app shouldn’t rely on any extended attributes. However, if it does then you shouldn’t be sequestering them. See Extended Attributes and Zip Archives.

Share and Enjoy

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

[1] FindMachO.sh is a script of my own devising; you can get a copy from Determining Why a Symbol is Referenced.

[2] Most of your other libraries don’t follow those rules. That’s not uncommon for apps that make heavy use of third-party tools (like PyInstaller) and libraries. These non-standard layouts can cause problems. In Placing Content in a Bundle you’ll find this fine quote:

If you put content in the wrong location, you may encounter hard-to-debug code signing and distribution problems. These problems aren’t always immediately obvious.

It may well be that, once we figure out what’s gone wrong here, this wonky placement is part of the problem. However, I’ve not seen it cause this specific pathology before. Normally the notary service is good about finding Mach-O images to notarise, regardless of how badly they’re misplaced.

Hi Quinn, thanks again for your great support. I uploaded in the feedback assistant ticket the notary log. I checked that b4563a07ac6827cced5dd13a172c41c80ca7d589 was in there to make sure that it was the correct one.

Surprisingly in my notary log it seems that all the Mach-O images are in the log.

I will try to fix the issues with content in the wrong location, do you have any suggestion on how to do package python apps correctly?

Surprisingly in my notary log it seems that all the Mach-O images are in the log.

Right. I suspect that my notarisation of your app has perturbed the system in some way. Given that, I’d like to try to get us back into the state where things are failing. Unfortunately that means that I have to get you to do some more work )-: Specifically:

  1. Rebuild and re-sign your app.

  2. Check that the top-level app has a different cdhash, that is, this command outputs something different:

    % codesign -d -vvv HotelOrganizer.app 
    …
    CDHash=b4563a07ac6827cced5dd13a172c41c80ca7d589
    …
    
  3. Notarise that.

  4. Grab the notary log and save that away.

  5. Staple and package the app.

  6. Reproduce the problem.

  7. Attach the new copy of your app and the notary log from step 4 to your bug report.

Reply back here when you’re done and I’ll take another look.


do you have any suggestion on how to do package python apps correctly?

No, sorry, I don’t maintain deep expertise in third-party tools like that.

Share and Enjoy

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

Accepted Answer

Thank you Quinn for your support.

I finally solved the issue. I checked the system log and found this error: syspolicyd: [com.apple.syspolicy.exec:default] Error Domain=GatekeeperPolicyScanError Code=-67018 "Code did not match any currently allowed policy" UserInfo={NSURL=file:///Applications/HotelOrganizer.app/Contents/Frameworks/numpy/_core/_multiarray_umath.cpython-311-darwin.so, NSLocalizedDescription=Code did not match any currently allowed policy}

I then tried to delete the file _multiarray_umath.cpython-311-darwin.so from my app bundle, and the gatekeeper finally accepted the app. BINGO!

However, without that files, my app crashes. Thus I tried to move this file from Frameworks folder to Resources folder and it also worked with gatekeeper. After some fighting with pyinstaller and other dependencies that were refusing to import numpy from the new location, I finally manage to defeat gatekeeper while having my app working well.

Now some feedback for Apple:

  1. I find it completely illogical that gatekeeper rejects notarised apps. The errors should appear during notarisation, not afterwards.
  2. If the errors appear in the system log, why aren't they reported also in the warning message of gatekeeper? This would make debugging 10x easier.
  3. syspolicy_check was useless in my case since it didn't provide any extra info that helped me to debug.
1. I find it completely illogical that gatekeeper rejects notarised apps.

You’re not the first person to make that observation. If you want to get that feedback to the folks who have the power to enact change, I recommend that you file a bug about it.

Please post your bug number, just for the record.

2. If the errors appear in the system log, why aren't they reported also in the warning message of gatekeeper?

Because Gatekeeper is targeted at users, not developers, and that information would be completely useless to the vast majority of users.

3. syspolicy_check was useless in my case since it didn't provide any extra info that helped me to debug.

Indeed. syspolicy_check is the right tool for investigating problems like this. We’re using your bug (FB19042844) to track improving it to debug this case.

Share and Enjoy

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

Gatekeeper rejects notarized app ("Unnotarized Developer ID") when using necessary entitlements
 
 
Q