Skip to content

[expo] Android spec hard-requires ClerkExpo native module at import time — crashes Expo Go even for JS-only flows #9197

Description

@warlock1987

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 ClerkProviderprovider/nativeClientSyncutils/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

  1. Expo SDK 57 managed app, @clerk/expo@3.7.8
  2. Wrap the root layout in <ClerkProvider publishableKey={…}> — JS-only email/password flow, no native components, no OAuth
  3. Open in Expo Go on Android → crash on launch
  4. 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.

Metadata

Metadata

Assignees

Labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions