Preliminary checks
- I have reviewed the documentation and searched existing issues — no existing report of this.
Reproduction / affected versions
@clerk/expo 3.4.0 through 3.7.8 (and the latest 3.7.9-canary as of 2026-07-18) — verified by unpacking each version's npm tarball.
Environment: Expo SDK 57, React Native 0.86, managed workflow, Expo Go on Android (emulator or device).
What happens
On Android in Expo Go, importing ClerkProvider crashes at module-load time, before any Clerk code runs:
Uncaught Error: Cannot find native module 'ClerkExpo'
Why
dist/specs/NativeClerkModule.android.js calls the throwing variant at module top level:
var NativeClerkModule_android_default = (0, expo.requireNativeModule)("ClerkExpo");
while the iOS/default dist/specs/NativeClerkModule.js uses requireOptionalNativeModule("ClerkExpo") (returns null when absent).
The guard in src/utils/native-module.ts clearly intends graceful degradation:
function loadNativeModule() {
if (!isNativeSupported) return null;
let nativeModule = null;
try {
nativeModule = src_specs_NativeClerkModule.default;
} catch (e) {}
if (isClerkExpoModule(nativeModule)) return nativeModule;
return null;
}
…but on Android the throw happens inside Metro's platform-resolved require("../specs/NativeClerkModule") — above that try/catch — so the guard never executes. The import chain ClerkProvider → provider/nativeClientSync → utils/native-module → spec is unconditional, so every Android Expo Go app crashes at startup regardless of which Clerk features it uses.
Expected
The Expo quickstart states the JavaScript-only custom-flow approach "works in Expo Go — no dev build required." That holds on iOS (optional require → null → guard handles it) but is currently impossible on Android.
Steps to reproduce
- Expo SDK 57 managed app,
@clerk/expo@3.7.8
- Wrap the root layout in
<ClerkProvider publishableKey={…}> — JS-only email/password flow, no native components, no OAuth
- Open in Expo Go on Android → crash on launch
- Same app in Expo Go on iOS → works
Suggested fix
Use requireOptionalNativeModule in src/specs/NativeClerkModule.android.ts (one line), letting the existing loadNativeModule() guard do its job. We've confirmed this works via a local pnpm patch — JS-only auth flows (email/password + email-code verification + sign-out, secure-store token cache) then run fine in Android Expo Go, and development builds that compile the module are unaffected since the optional require returns it when present.
Preliminary checks
Reproduction / affected versions
@clerk/expo3.4.0 through 3.7.8 (and the latest3.7.9-canaryas of 2026-07-18) — verified by unpacking each version's npm tarball.Environment: Expo SDK 57, React Native 0.86, managed workflow, Expo Go on Android (emulator or device).
What happens
On Android in Expo Go, importing
ClerkProvidercrashes at module-load time, before any Clerk code runs:Why
dist/specs/NativeClerkModule.android.jscalls the throwing variant at module top level:while the iOS/default
dist/specs/NativeClerkModule.jsusesrequireOptionalNativeModule("ClerkExpo")(returnsnullwhen absent).The guard in
src/utils/native-module.tsclearly intends graceful degradation:…but on Android the throw happens inside Metro's platform-resolved
require("../specs/NativeClerkModule")— above that try/catch — so the guard never executes. The import chainClerkProvider→provider/nativeClientSync→utils/native-module→ spec is unconditional, so every Android Expo Go app crashes at startup regardless of which Clerk features it uses.Expected
The Expo quickstart states the JavaScript-only custom-flow approach "works in Expo Go — no dev build required." That holds on iOS (optional require →
null→ guard handles it) but is currently impossible on Android.Steps to reproduce
@clerk/expo@3.7.8<ClerkProvider publishableKey={…}>— JS-only email/password flow, no native components, no OAuthSuggested fix
Use
requireOptionalNativeModuleinsrc/specs/NativeClerkModule.android.ts(one line), letting the existingloadNativeModule()guard do its job. We've confirmed this works via a localpnpm patch— JS-only auth flows (email/password + email-code verification + sign-out, secure-store token cache) then run fine in Android Expo Go, and development builds that compile the module are unaffected since the optional require returns it when present.