Steps to reproduce:
1. Run tuist init
2. In Tuist/Package.swift, add:
.package(url: "https://github.com/getsentry/sentry-cocoa", exact: "9.16.1")
3. In Project.swift, add the dependency to the main target:
dependencies: [.external(name: "Sentry")]
4. Run tuist install
5. Run tuist generate
6. In Xcode, add the following code:
import Sentry
// Add this somewhere:
SentrySDK.start { options in }
7. In Xcode press Cmd+B → Builds OK 
8. Downgrade the version in Tuist/Package.swift to exact: "8.58.3"
9. Run tuist install
10. Run tuist generate
11. In Xcode press Cmd+B → Build Fails 
BUT: If I do an Xcode Clean at this point, it builds fine.
Expected behavior:
After running tuist install and tuist generate with the updated version, Xcode should build successfully without requiring a manual clean.
Environment:
Tuist version: 4.196.0
Thanks!
Hey!
Can you provide a reproducible sample and provide us with the exact Xcode versions? I wasn’t able to reproduce the issue on our side based on this description.
I was able to reproduce the issue deterministically now, and the short version is: this is an Xcode bug, not a Tuist one. It reproduces with plain SwiftPM and no Tuist involved.
Reproduction
On Xcode 26.5 + Tuist 4.196.0, the failure is reliable once you go back and forth between the two versions:
clean build @ Sentry 8.58.3 → ✅switch → 9.16.1, tuist install + generate, build → ✅switch → 8.58.3, tuist install + generate, build → ❌ missing required module 'CommonCrypto'
The failing step is always the downgrade, and only after at least two switches. A single switch, or starting clean on 9.16.1, doesn’t trigger it, which lines up with your observation that it worked at first and then started happening after toggling a few times.
What’s actually going on
It’s Xcode’s explicit modules incremental build reusing a stale precompiled module from the module cache:
- Sentry 9.16.1’s
Sentry.swiftinterface does import CommonCrypto. 8.58.3’s does not (their module.modulemap differs too).
- After you downgrade, the explicit module map Xcode hands to the Swift frontend correctly contains no
CommonCrypto entry, but it still points Sentry at a precompiled Sentry-*.swiftmodule left over from the 9.16.1 build that still records a requirement on CommonCrypto (and SwiftUI). The frontend can’t satisfy it, so you get missing required module 'CommonCrypto'.
- Because the binary
xcframework is vendored at a version-independent path, the framework search path never changes across versions, so Xcode’s scanner doesn’t re-derive the module and the stale one sticks around until you Clean.
Why it’s not Tuist
I built a tiny vanilla SwiftPM package with the same Sentry binary dependency and no Tuist at all, then ran the exact same back-and-forth via xcodebuild. It fails identically (missing required modules: 'CommonCrypto', 'SwiftUI'). Native SwiftPM vendors binary artifacts at the same kind of stable path, so it hits the same cache staleness. I’d recommend filing this with Apple as an Xcode explicit-modules / binary-target incremental-build bug.
Workarounds
All three of these resolve it, verified:
- Xcode Clean (what you’re doing today).
- You don’t actually need a full Clean. Wiping just the module cache is enough:
rm -rf <DerivedData>/Build/Intermediates.noindex/SwiftExplicitPrecompiledModulesrm -rf <DerivedData>/ModuleCache.noindex
- The cleanest one for CI: disable explicit modules by setting
SWIFT_ENABLE_EXPLICIT_MODULES=NO. With that, the full back-and-forth sequence builds end to end with no clean. You can set it as a build setting in your Project.swift on the affected target(s). The tradeoff is you lose the explicit-modules build-performance improvements, but it sidesteps the cache bug.
Hope that unblocks your CI. Happy to help wire up option 3 in the project if useful.
note: answer generated by Claude