Hive Hive
Sign in

XcodeGraphMapper traps on a local package that is also a folder reference

GitHub issue · Open

Metadata
Source
tuist/tuist #12546
Updated
Aug 21, 2026
Domains
Generated projects
Details

What happened

XcodeGraphMapper.map(at:) aborts the process with a trap when a project contains a local Swift package that is also present as a folder reference in the project navigator:

Swift/NativeDictionary.swift:792: Fatal error: Duplicate values for key: '/path/to/App/Packages/MyPackage'
Thread 2 crashed:
1 specialized _NativeDictionary.merge<A>(_:isUnique:uniquingKeysWith:)
2 Dictionary.init<A>(uniqueKeysWithValues:)
3 closure #1 in XcodeGraphMapper.extractPackages(from:)
XcodeGraphMapper.swift:237
4 Dictionary.compactMapValues<A>(_:)
5 XcodeGraphMapper.extractPackages(from:) XcodeGraphMapper.swift:235
6 XcodeGraphMapper.buildGraph(from:) XcodeGraphMapper.swift:136
7 XcodeGraphMapper.map(at:) XcodeGraphMapper.swift:82

Because this is a trap rather than a thrown error, consumers cannot recover from it — the whole process dies. In our case that takes an Xcode prebuild phase with it, and the only actionable output the user gets is the line above.

Why

Local packages are collected from two independent places in PBXProjectMapper.map(...):

let localPackages = try pbxProject.localPackages.compactMap {
try packageMapper.map(package: $0, sourceDirectory: sourceDirectory)
} + localPackagePaths.map { .local(path: $0) }
  • pbxProject.localPackages — the XCLocalSwiftPackageReference entries.
  • localPackagePaths — from collectAllPackages(from:xcodeProj:), which walks the group tree and treats every PBXFileReference pointing at a directory that contains a Package.swift as a package.

A local package that is declared as a package reference and shown as a folder in the navigator is therefore mapped twice, with the same path. extractPackages then keys packages by URL and requires those keys to be unique:

// XcodeGraphMapper.swift:235
private func extractPackages(from projects: [AbsolutePath: Project]) -> [AbsolutePath: [String: Package]] {
projects.compactMapValues { project in
guard !project.packages.isEmpty else { return nil }
return Dictionary(uniqueKeysWithValues: project.packages.map { ($0.url, $0) })
}
}

Reproduction

Any project that carries both entries for one package. A project generated by XcodeGen from

packages:
MyPackage:
path: Packages/MyPackage
targets:
App:
dependencies:
- package: MyPackage
product: MyPackage

produces exactly that — the XCLocalSwiftPackageReference that links the package, plus a folder reference so it appears in the navigator:

/* Begin PBXFileReference section */
0F5C85DC… /* MyPackage */ = {isa = PBXFileReference; lastKnownFileType = folder; name = MyPackage; path = Packages/MyPackage; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
/* Begin XCLocalSwiftPackageReference section */
FE0DA337… /* XCLocalSwiftPackageReference "Packages/MyPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Packages/MyPackage;
};
/* End XCLocalSwiftPackageReference section */

Removing the folder reference alone makes the crash disappear, which is what pinpoints the duplicate.

Suggested fix

Two places, either of which prevents the trap:

  • PBXProjectMapper: de-duplicate before constructing the project, e.g. keep the first Package per URL when combining localPackages with localPackagePaths — the two collection paths genuinely describe the same package.
  • XcodeGraphMapper.extractPackages: use Dictionary(_:uniquingKeysWith:) instead of uniqueKeysWithValues:. Even with the de-duplication above, a project file is user-supplied input; aborting the process on unexpected content is harsh for a library.

Happy to open a PR for either or both if that is welcome.

Environment

  • XcodeGraph 1.34.5 (from the archived tuist/XcodeGraph; the same code is in this repository at cli/Sources/XcodeGraph/Sources/XcodeGraphMapper/, unchanged as of today — extractPackages at line 237 and the two collection paths in PBXProjectMapper around line 109)
  • macOS 26.6.1, Xcode 27.0
  • Reached through Shark, which uses XcodeGraphMapper to find a target’s resources

Filed here because tuist/XcodeGraph is archived and read-only.

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
D
dosubot[bot] Aug 21, 2026

@mickeyl thanks for the thorough write-up — the analysis is spot-on.

The bug is confirmed in the current codebase. In PBXProjectMapper.swift at line 103–109, localPackages is assembled by concatenating pbxProject.localPackages with localPackagePaths without any de-duplication. Then extractPackages in XcodeGraphMapper.swift uses Dictionary(uniqueKeysWithValues:), which traps on duplicate keys — exactly the crash you’re seeing.

Your two suggested fixes both look correct. The safer one — switching extractPackages to Dictionary(_:uniquingKeysWith:) (keeping the first occurrence) — turns a process-killing trap into graceful handling of user-supplied input and should be there regardless. The upstream de-duplication in PBXProjectMapper is the right semantic fix on top of that.

A PR fixing either or both would be very welcome — please go ahead and target tuist/tuist since tuist/XcodeGraph is archived.

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

A
aryansk Aug 23, 2026

Thanks for the report — the duplicate-key trap is clear. I’ll put up a fix.