I hit a wall in Xcode the other day that didn't make sense. I had two Swift packages already wired into an iOS project, both hosted on my private Forgejo instance over SSH, both resolving fine. Then I tried to add two new packages from the same instance, with the same SSH config, into the same project, and Xcode refused with an error that has by now become something of a meme:
unexpectedly did not find the new dependency in the package graph
That's all you get. No fingerprint prompt, no auth failure, no parse error. The package is there. The tag exists. The cache fetches it. And Xcode still reports that it cannot find the dependency in the graph it just built.
If you've landed here, you've probably been staring at the same error for an hour. Let me save you that hour.
The setup
To give you a sense of the environment where I hit this:
- Xcode 26.5 RC (Build 17F42)
- A private Forgejo server reachable over SSH (
ssh://git@<your-host>/iOS_Packages/<Package>.git) - A handful of small Swift packages that the iOS app pulls in via SPM
- Annotated tags like
0.0.1,0.0.2
Two packages had been added to the project some weeks earlier and worked fine. Two new packages, structured the same way, refused to add. Same host, same auth, same project.
It's not specific to Forgejo either. Folks running into this on GitLab and Gitea private instances report exactly the same symptoms.
What the error actually means
The phrasing is misleading. Xcode is not telling you that it could not fetch the package. It is telling you that it tried to add the new entry to the dependency graph and, after resolution, the entry was not there.
Roughly what happens behind the UI:
- You paste the SSH URL and pick a version
- Xcode triggers SwiftPM to fetch and resolve the new package alongside everything already pinned
- SwiftPM clones over SSH using Xcode's bundled Git transport
- The clone trips on host-key verification inside that transport on Xcode 26
- SwiftPM silently rolls back and returns an empty result
- Xcode looks at the resolved graph, does not find the new identity, and surfaces the generic "did not find" error
You don't see the actual SSH failure because the failure happens inside Xcode's internal SCM layer, not in the system git binary you'd normally use from a terminal.
If your error looks slightly different and reads "Server SSH Fingerprint Failed to Verify" or "SSH Key failed to verify", you're hitting the same underlying bug, only Xcode surfaced the real cause directly instead of swallowing it into the graph error. The fix below works for both variants.
How I confirmed it wasn't my package
This was the first thing I needed to be sure of. If the issue is in my Package.swift, no Xcode tweak is going to help me.
The diagnostic was running SwiftPM from the command line against the same URL and tag:
mkdir /tmp/probe && cd /tmp/probe
cat > Package.swift <<'EOF'
// swift-tools-version: 6.2
import PackageDescription
let package = Package(
name: "Probe",
platforms: [.iOS(.v26), .macOS(.v26)],
dependencies: [
.package(
url: "ssh://git@<your-host>/<your-org>/<Package>.git",
from: "0.0.1"
)
],
targets: [
.target(name: "Probe", dependencies: [
.product(name: "<Package>", package: "<Package>")
])
]
)
EOF
mkdir -p Sources/Probe && echo "import <Package>" > Sources/Probe/Probe.swift
swift package resolve
If swift package resolve exits cleanly and writes Package.resolved, the package is fine and the bug is somewhere in Xcode. For me it succeeded instantly. Same SSH URL, same tag, same machine. Just outside Xcode.
That's the moment to stop blaming your Package.swift.
The fix
Xcode 26 has a hidden default that controls which Git/SCM stack it uses for Swift packages. By default, it goes through a code path that handles SSH host-key verification in a way that trips on new packages from self-hosted servers. The workaround is to flip Xcode over to its own built-in SCM, which gets past the verification step reliably.
Run this in a terminal:
defaults write com.apple.dt.Xcode IDEPackageSupportUseBuiltinSCM 1
Then fully quit Xcode so the new default is picked up on the next launch. Use Cmd-Q, not just close the window.
Before reopening, wipe two cache layers SwiftPM keeps. These survive most "Reset Package Caches" attempts from the Xcode menu, which is part of why this bug feels so sticky:
# Global SwiftPM repo cache for the package(s) that won't resolve
rm -rf ~/Library/Caches/org.swift.swiftpm/repositories/<Package>-*
# Per-project SourcePackages cache inside DerivedData
rm -rf ~/Library/Developer/Xcode/DerivedData/<YourApp>-*/SourcePackages
Reopen the project, retry File > Add Package Dependencies..., paste the same SSH URL, pick the same version. It should resolve immediately.
The setting is reversible. If anything else starts misbehaving, defaults delete com.apple.dt.Xcode IDEPackageSupportUseBuiltinSCM puts you back on the default behavior.
Why the old packages still worked
This was the part that confused me longest. If Xcode's SCM is broken, why did the two packages I added weeks ago resolve fine when I reopened the project?
Once a package is pinned in Package.resolved, Xcode appears to skip re-running host-key verification on it. It trusts the existing pinned commit and works off the cache. The bug only triggers when Xcode has to talk to a new URL it has never resolved before.
So the "everything works for existing packages, only new adds break" symptom is a feature of the bug, not evidence against it. It also explains why bumping the version of an already-added package usually works while adding a brand-new package fails.
A few diagnostics worth knowing
If you want to go a level deeper before applying the fix, these are quick read-only checks that helped me convince myself I knew what I was dealing with:
# Tags on remote, is the version actually there?
git ls-remote --tags ssh://git@<your-host>/<your-org>/<Package>.git
# Tags in Xcode's cached bare clone, does Xcode know about them?
git -C ~/Library/Caches/org.swift.swiftpm/repositories/<Package>-* tag -l
# Can system SSH reach the host at all?
ssh -T git@<your-host>
If remote has the tag, cache does not, and SSH works at the terminal, then you're looking at the Xcode SCM bug.
Final Thoughts
The error message points everywhere except at the real culprit, which is what made this so frustrating. Once you know that Xcode 26 ships with an SCM default that disagrees with your system SSH, the fix is one terminal command and a cache wipe. If this saved you an hour, then it did its job. And if you start writing more iOS code against a self-hosted Git server, bookmark this one. It will come back.
Photo by Kostiantyn Li on Unsplash
No comments yet
Be the first to share your thoughts on this article!