Repository navigation
[Pro 1.7.0] Bundling rewrites installed CSS and changes Expo fingerprints, preventing OTA delivery #672
Description
What happened?
With runtimeVersion: { policy: "fingerprint" }, bundling our Expo app changes the fingerprint of node_modules/uniwind without any native code or dependency changes. Generated CSS is written back into the installed package, which Expo fingerprints because Uniwind Pro autolinks native code.
In our EAS workflow, updates fingerprint after expo export, while the build fingerprints a clean install. This caused OTA updates to be published for a runtime version our installed builds did not carry, so users never received them. We also hit a build-time "Runtime version mismatch" when starting a build from a local tree where Metro had already run.
Related: #487 reported fingerprint drift and was attributed to RC artifacts changing on the CDN. This report identifies a separate trigger on stable Pro 1.7.0: writes to the installed package during bundling, without downloading or changing the dependency version.
Evidence from our investigation:
- Comparing the update and build fingerprints isolated the differing dependency source to
{"type":"dir","filePath":"../../node_modules/uniwind","reasons":["rncoreAutolinkingIos"]}. - Comparing the installed package with the pristine Pro 1.7.0 tarball isolated the generated CSS content change to
uniwind.css, apart from our known postinstall patch/installer files.animations.cssis also rewritten, but was byte-identical in our comparison. - Appending a comment to the installed
uniwind.csschanges the iOS fingerprint. Ignoring the two generated CSS files prevents that change.
Write sites in the shipped bundler:
-
src/bundler/adapters/metro/transformer.tsresolves../../uniwind.cssrelative to its bundled location and callsgenerateArtifacts(cssArtifactPath)when transforming the CSS entry file. The repro uses this real Metro path without patching it. -
dist/module/bundler/artifacts/css/index.jswrites compiled theme CSS to the supplied output path. -
dist/module/bundler/artifacts/css/build-animations-css.jsresolvesanimations.cssfromrequire.resolve("uniwind/package.json")and writes it unconditionally. Its identical content is not itself the hash drift we observed. -
The artifact-generation CLI also derives its CSS output path from its own package location (
dist/module/bundler/cli/index.js). The same package-relative path exists in the publicpackages/uniwind/src/bundler/cli/index.ts.
Suggested fix: keep generated CSS outside the installed package, in an app-owned cache directory, and update CSS resolution accordingly. If animation CSS is constant, shipping it without rewriting it may be sufficient for that file. Generated theme CSS should not modify a directory included in native autolinking fingerprints.
Happy to open a PR for this, including a regression test that artifact generation leaves the installed package unchanged, if this approach works for you.
Current workaround: our app's .fingerprintignore includes the following (paths reflect our monorepo layout):
../../node_modules/uniwind/uniwind.css
../../node_modules/uniwind/animations.cssThis keeps native Uniwind files fingerprinted. We can remove these exclusions once an upstream fix ships.
Steps to Reproduce
Minimal repository: https://github.com/invivek26/uniwind-fingerprint-drift-repro
Requires your normal Uniwind Pro installation setup. No EAS account, simulator, native build, or running dev server is needed.
git clone https://github.com/invivek26/uniwind-fingerprint-drift-repro
cd uniwind-fingerprint-drift-repro
bun install
node repro.mjsRun before starting Metro or exporting manually. The script uses a real Expo export with the Metro cache cleared, fingerprints before/after, asserts that only the autolinked Uniwind directory changes, and checks that excluding the two generated CSS files prevents the drift. It restores the original uniwind.css afterward so the repro can be repeated.
Verified twice using the pristine cached Pro 1.7.0 package and existing local dependencies (no fresh dependency install performed):
Before export: 4d9c79754f5cd40c8370f9ca0001cf07dadc5870
After export: 0a3f4ea28d5788a45a6341b100ccf9edc5ff96db
Only changed source: node_modules/uniwind (rncoreAutolinkingIos)
Ignoring generated CSS:
016c70bf3d1539333c186566f96c219b72fe56f3
016c70bf3d1539333c186566f96c219b72fe56f3
REPRODUCED: exporting changes only Uniwind's native fingerprint; the CSS exclusions prevent it.
Expected: an export alone leaves the native runtime fingerprint unchanged.
Actual: the fingerprints differ, with node_modules/uniwind as the only changed source. Absolute hashes vary by installation; the script asserts the single-source diff and unchanged control fingerprint.
Snack or Repository Link (Optional)
https://github.com/invivek26/uniwind-fingerprint-drift-repro
Uniwind version
npm:uniwind-pro@1.7.0
React Native Version
0.86.3
Platforms
iOS (verified). Android may be affected through native autolinking as well, but we have not independently verified its fingerprint diff.
Expo
Yes — SDK 57 (57.0.20 in the repro). Original investigation: @expo/fingerprint 0.19.4; standalone repro also confirms it on 0.20.12.
Additional information 〰
- I've searched for similar issues; changing expo fingerprint hash and preventing expo-updates #487 is related, with a different reported cause as explained above.
- I am a Uniwind Pro user.
Activity
invivek26 commentedon Sep 8, 2026
Added a minimal reproduction repository and updated the report: https://github.com/invivek26/uniwind-fingerprint-drift-repro
After the normal Pro installation setup, run
bun installthennode repro.mjs. It runs a real iOS Expo export with Metro's cache cleared and asserts that:node_modules/uniwindfingerprint changes.Verified twice with pristine Pro 1.7.0 and existing local dependencies; the README includes the output and verification details. No native build or EAS account is needed.
Happy to open a PR moving the generated CSS outside the installed package, with a regression test, if that approach works for you.
Brentlok commentedon Sep 21, 2026
This would be a breaking change, because you would need to import this css generated outside of the installed package separately and we don't want to release new major version for single breaking change
I think that you could just add
uniwind.cssto.fingerprintignorefile https://docs.expo.dev/versions/latest/sdk/fingerprint/#fingerprintignore just like you've said