Hive Hive
Sign in

SwifterPM can’t read registry credentials from login keychain in headless CI environment

GitHub issue · Open

Metadata
Source
tuist/tuist #12267
Updated
Aug 11, 2026
Domains
Generated projects
Details

What happened?

On Tuist versions up to 4.202.x, we are using a private, authenticated registry endpoint via the following (simplified) steps:

swift package-registry login "https://{registry URL here}" --token={registry token here} --no-confirm
tuist install

This has worked fine for us on all previous Tuist versions. However, when switching to 4.203 (or opting into SwifterPM via the environment flags) seems to break things. Now, the tuist install hangs in CI, and if I remotely access our CI machines, I see the following UI prompt:

How do we reproduce it?

Set any Tuist project to utilize a registry which requires authentication, and store the required credentials in the Login keychain by using swift package-registry login "https://{registry URL here}" --token={registry token here} --no-confirm. Then try to run a tuist install in a non-interactive environment.

Error log

The install hangs, and a UI prompt is shown on the headless machine.

macOS version

26.5.2

Tuist version

4.203.4

Xcode version

26.2

Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

Comments
S
sphanley Aug 11, 2026

I also tried creating a temporary keychain, as described in https://tuist.dev/en/docs/guides/features/registry/continuous-integration. However, this resulted in the following error:

✖ Error HTTP 401 for https://{private package URL} Sorry this didn’t work. Here’s what to try next: ▸ If the error is actionable, address it ▸ If the error is not actionable, let’s discuss it in the <Troubleshooting & how to: https://community.tuist.dev/c/troubleshooting-how-to/6> ▸ If you are very certain it’s a bug, <file an issue: https://github.com/tuist/tuist> ▸ Check out the logs at /Users/ec2-user/.local/state/tuist/sessions/279CC8CD-73DD-4C15-8244-0E61C86822D5/logs.txt mise ERROR sh failed

Is it possible that SwifterPM isn’t reading from the default keychain in cases when it’s changed?

D
dosubot[bot] Aug 11, 2026

@sphanley Yes, that’s very likely the issue. Here’s what’s happening:

When you run swift package-registry login, it stores credentials in whichever keychain is the default at that time — typically the login keychain. Later, if you create a temporary keychain and set it as default (per the CI guide), SwifterPM’s Keychain lookup searches the default keychain search list — which now points at your temporary keychain, not the login keychain where the credentials actually live [1]. So the credentials aren’t found, and you get the 401.

To fix this, you’d need to either:

  1. Store registry credentials in the temporary keychain — run swift package-registry login after creating and setting the temporary keychain as default, so the credentials land in the right place.
  2. Add the login keychain back to the search list — after creating the temporary keychain, run security list-keychains -s "$TEMP_KEYCHAIN" ~/Library/Keychains/login.keychain-db so both keychains are searchable.
  3. Skip the keychain entirely — use environment variables instead, which take priority over keychain lookups [1]:
    export SWIFTPM_REGISTRY_TOKEN="{your token}"
    tuist install

Option 3 is probably the simplest for CI. The env var SWIFTPM_REGISTRY_TOKEN is checked before the keychain, so it avoids all keychain-related issues.

If this doesn’t resolve things or you need to dig deeper, I’d suggest posting in Troubleshooting & how-to with the details of your CI keychain setup and the log file referenced in the error.

To reply, just mention @dosu.


Share context across your team and agents. Try Dosu.

Leave Feedback Learn about tuist with Dosu Add Dosu to your team

S
sphanley Aug 11, 2026

@dosu, to clarify, I’m creating the temporary keychain and setting it as the default before calling swift package-registry login. But SwifterPM is still showing a 401 error on package resolution, leading me to believe that it is not finding the credential in the temporary keychain.

I agree that using SWIFTPM_REGISTRY_TOKEN (or SWIFTPM_NETRC_DATA) would be an ideal workaround, but these environment variables do not seem to be available in Swift 6.2.3, which ships with Xcode 26.2. Once my projects are compatible with Swift 6.3, that will be a good workaround approach.

D
dosubot[bot] Aug 11, 2026

@sphanley Thanks for the clarification — that’s helpful context.

Good news on the env vars: SWIFTPM_REGISTRY_TOKEN and SWIFTPM_NETRC_DATA are actually handled directly by Tuist’s SwifterPM code, not by Apple’s SwiftPM [1]. They’re read via ProcessInfo.processInfo.environment inside SwifterPM itself, so they work regardless of your Swift version. You should be able to use them with Swift 6.2.3 / Xcode 26.2 today.

On the keychain issue: The 401 is coming from SwifterPM’s own HTTP client (not the swift package resolve subprocess) [2], which means SwifterPM’s keychain lookup is failing to find the credentials you stored. SwifterPM queries for kSecClassInternetPassword entries matching the registry’s protocol, host, and optional port [1] — if swift package-registry login stores credentials with different attributes (e.g., a different item class, or a path component SwifterPM doesn’t query for), there’d be a mismatch even when the temporary keychain is correctly set as default. This looks like a genuine compatibility gap between what swift package-registry login writes and what SwifterPM reads.

Immediate workaround: Try setting SWIFTPM_REGISTRY_TOKEN in your CI environment — it’s checked before the keychain and should bypass the issue entirely:

export SWIFTPM_REGISTRY_TOKEN="{your token}"
tuist install

If you’d like to dig deeper into the keychain attribute mismatch or discuss further, I’d recommend posting in the Troubleshooting & how-to category on the community forum, focusing on the keychain attribute details and your CI keychain setup steps.

To reply, just mention @dosu.


Share context across your team and agents. Try Dosu.

Leave Feedback Learn about tuist with Dosu Add Dosu to your team

S
sphanley Aug 11, 2026

@dosu thank you for the clarification. I’ve retried using SWIFTPM_REGISTRY_TOKEN and it does seem to be working now. I had issues with it on a previous Tuist version, but perhaps it was user error on my part.

In either case, the original question/bug report still stands in terms of a functional gap between SwifterPM and the prior package resolution implementation.

D
dosubot[bot] Aug 11, 2026

@sphanley Glad SWIFTPM_REGISTRY_TOKEN is working for you now!

And agreed — the underlying issue is real. The prior implementation (Apple’s SwiftPM in-process) could read credentials stored by swift package-registry login directly from the keychain, whereas SwifterPM’s own keychain query appears to have an attribute mismatch with what swift package-registry login writes. That’s a regression in behavior that should ideally be addressed so that the keychain-based workflow continues to work seamlessly, especially for users who can’t easily switch to env vars.

I’ll leave this for the maintainers to triage. The core bug is that SwifterPM’s SecItemCopyMatching query [1] doesn’t find credentials stored by swift package-registry login, even when those credentials are in a keychain on the default search list.

To reply, just mention @dosu.


Docs are dead. Just use Dosu.

Leave Feedback Ask Dosu about tuist Share Dosu with your team