Skip to content

Stale explicit-module cache after Rust dep bumps surfaces as bogus UniFFI "cannot find checksum func" errors #647

Description

@jvsena42

Summary

Any bump of a Rust/UniFFI dependency (bitkit-core, ldk-node, paykit) can leave Xcode's explicit precompiled module cache stale, which makes the build fail with a confusing cascade of errors that look like a broken binary artifact rather than a cache problem.

This is a known, recurring developer-environment issue. Filing it so the workaround is discoverable and so we can decide on a permanent fix.

Symptoms

The build first reports something like:

error: file '<DD>/SourcePackages/artifacts/.../paykitFFI/module.modulemap' has been modified since the module file ... .pcm was built
note: size changed from expected 128 to 129

(or the same error for bitkitcoreFFI.h / bitkitcoreFFI/module.modulemap)

and then cascades into what looks like a completely different failure:

error: cannot find 'uniffi_bitkitcore_checksum_func_<name>' in scope

in bitkit-core/bindings/ios/bitkitcore.swift. The second error is the misleading one — it is identical to the symptom of a genuinely stale SPM binary artifact (a re-cut release tag serving a cached zip), so the usual instinct is to go wipe the SPM cache, which is a much heavier and unnecessary operation.

Cause

Xcode's explicit-modules .pcm files are keyed by the modulemap/header's size and mtime. UniFFI-generated modulemaps change size on essentially every dependency bump (new FFI symbols ⇒ new header/modulemap bytes), so every bump invalidates the .pcm — but Xcode does not always rebuild it, and File ▸ Packages ▸ Reset Package Caches does not clear it.

Workaround (lightweight, verified)

rm -rf <DerivedData>/Build/Intermediates.noindex/SwiftExplicitPrecompiledModules
rm -rf <DerivedData>/../ModuleCache.noindex
# then rebuild

Notes:

  • A first rebuild after this may fail with There is no XCFramework found at .../<Name>.xcframework — that is just an SPM re-extraction race; the next rebuild succeeds.
  • Heavier fallback if the above is not enough: rm -rf <DerivedData>/Build/Intermediates.noindex <DerivedData>/Build/Products.
  • Only if the failing symbol is genuinely absent from the downloaded binary is a full SPM cache wipe (~/Library/Caches/org.swift.swiftpm, ~/.swiftpm/cache, SourcePackages) warranted. Verify first, e.g.:
    grep -rl <missing_symbol_name> <DerivedData>/SourcePackages/artifacts/bitkit-core/BitkitCoreFFI/BitkitCore.xcframework

Verified on 2026-07-13 with a paykitFFI modulemap going 128 → 129 bytes.

Proposed fixes

  1. Add a scripts/clear-module-cache.sh that performs the lightweight cleanup, and document that it should be run after every Rust dependency bump (README / AGENTS.md).
  2. Or set SWIFT_ENABLE_EXPLICIT_MODULES = NO for the project, trading some build-time caching for not hitting this class of failure at all.
  3. Optionally have the script also detect the "symbol missing from the xcframework" case and print the SPM-wipe recovery steps, so the two failure modes stop being conflated.

Environment

  • Xcode with explicit modules enabled (default)
  • Affects any Rust/UniFFI dependency bump: bitkit-core, ldk-node, paykit

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions