SecItemAdd returns OSStatus 100001 from JXA launched by a sandboxed development tool

On macOS 26.6.2 (build 25G83), arm64, I am using the Security framework through JXA, executed by a static /usr/bin/osascript helper launched from the Codex desktop application's local command environment.

A dummy-only test attempts to add one generic-password item to the local file-based login keychain. SecItemAdd returns OSStatus 100001. The system's "security error 100001" command describes this as "UNIX[Operation not permitted]".

The helper explicitly opens the login keychain and selects it using kSecUseKeychain for the add operation. Synchronizable and Data Protection Keychain options are false. It supplies an access object created by SecAccessCreate with an empty trusted-application array.

What I have confirmed:

  • Keychain open/status calls succeed and report unlocked, readable, and writable. I understand these flags do not establish permission for this individual operation.
  • Attribute-only exact lookups report the dummy item as not found before and after each failed add.
  • Granting narrowly scoped filesystem write permission for the login keychain file through the launching tool did not resolve the failure.
  • I have not performed a comparison outside that restricted execution environment. I have not established whether the cause is the environment, API/ACL configuration, or an OS issue.

Is this JXA/Security usage supported for a file-based keychain? Which documented diagnostics can distinguish process/sandbox restrictions from item ACL or authentication requirements? Are there relevant constraints on this query/access-object construction?

I am seeking a supported implementation, not a way to disable or bypass security protections. I can provide a dummy-only reproducer without credentials or personal paths.

Answered by DTS Engineer in 906950022

Thanks for all that info.

I did some digging and my best guess is that Codex is applying a custom sandbox, using sandbox-exec and their own SBPL code. We don’t support that, for the reason I explain in this thread.

Given that, there are limits to how much I can help you here. I suspect that your JAX code is inheriting this custom sandbox from Codex, and that’s the root cause of this problem. And that has a couple of negative consequences:

  • I can’t help you with your problem, because I’ve no idea how Codex has set up this sandbox.
  • I normally encourage folks who are having problems like this to contact their tooling vendor and, if necessary, the tooling vendor can seek help from Apple. However, that won’t work in this case because we can’t help them with SBPL [1].

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

[1] Our advice for folks who want to implement custom sandboxing is to lean in to the es_new_descendants_client API we added in macOS 27.

Did this work in older versions of macOS? Or are you creating something new and it just happens to be on macOS 26.6.2?

ps Error 100001 is the Security framework version of EPERM. See QA1499 Security Framework Error Codes. EPERM is typically indicates an App Sandbox limit, but you can get it with MAC as well. See On File System Permissions

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

Thank you. I confirmed that this Mac is running macOS 26.6.2 (build 25G83). This is a new Keychain integration. We have not tested it on earlier macOS versions, so we cannot confirm whether this is an OS regression. What diagnostic logs or checks would you recommend to distinguish an inherited App Sandbox restriction from MAC?

I’d like to get a better understanding of how your code ends up running. You wrote:

I am using the Security framework through JXA, executed by a static /usr/bin/osascript helper launched from the Codex desktop application's local command environment.

I presume that:

  • The “Codex desktop application” is the product from OpenAI.
  • And that you’re just using this app, not a developer working on it.
  • “JXA” means “JavaScript for Automation” in this context.

What the actual execution chain look like? Does Codex call osascript directly? And are you then accessing the keychain from the JXA JavaScript running within osascript? Or is there another level of indirection?

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

Thank you for following up. Yes, all three assumptions are correct:

  • I am using OpenAI’s Codex desktop application.
  • I am a user of Codex, not a developer working on Codex itself.
  • JXA means JavaScript for Automation.

To clarify my earlier description, there is an intermediate Node.js process belonging to the application I am developing. The execution chain is:

Codex’s local command execution environment → my application’s Node.js process → Node.js child_process.spawn(..., { shell: false }) → /usr/bin/osascript -l JavaScript <path-to-fixed-script> → JXA calls the Security framework → the local login keychain

So, in this implementation, my Node.js process launches osascript; Codex does not directly invoke it.

The fixed JXA script runs inside osascript and uses ObjC.import('Security') to call the Security framework. It explicitly opens ~/Library/Keychains/login.keychain-db using SecKeychainOpen. It does not use the Data Protection Keychain or iCloud synchronization.

When testing the addition of a dummy generic-password item, SecItemAdd returned OSStatus 100001.

I have not yet established the exact internal process hierarchy of Codex’s command execution environment or which restrictions may be inherited by the child processes.

What process information or diagnostic evidence would be most useful to collect next to identify the source of this permission denial?

Thanks for all that info.

I did some digging and my best guess is that Codex is applying a custom sandbox, using sandbox-exec and their own SBPL code. We don’t support that, for the reason I explain in this thread.

Given that, there are limits to how much I can help you here. I suspect that your JAX code is inheriting this custom sandbox from Codex, and that’s the root cause of this problem. And that has a couple of negative consequences:

  • I can’t help you with your problem, because I’ve no idea how Codex has set up this sandbox.
  • I normally encourage folks who are having problems like this to contact their tooling vendor and, if necessary, the tooling vendor can seek help from Apple. However, that won’t work in this case because we can’t help them with SBPL [1].

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

[1] Our advice for folks who want to implement custom sandboxing is to lean in to the es_new_descendants_client API we added in macOS 27.

SecItemAdd returns OSStatus 100001 from JXA launched by a sandboxed development tool
 
 
Q