-
Notifications
You must be signed in to change notification settings - Fork 444
@clerk/expo v3: useAuth().isLoaded permanently false - works in minimal app, fails in real app (physical device + simulator) #8245
Description
Preliminary Checks
-
I have reviewed the documentation: https://clerk.com/docs
-
I have searched for existing issues: https://github.com/clerk/javascript/issues
-
I have not already reached out to Clerk support via email or Discord (if you have, no need to open an issue here)
-
This issue is not a question, general help request, or anything other than a bug report directly related to Clerk. Please ask questions in our Discord community: https://clerk.com/discord.
Reproduction
I cannot repro this issue as a fresh project as per the QuickStart guide does work. I can give you access to my project however if that is helpful for you
Publishable key
pk_test_d2FudGVkLWdhemVsbGUtNDguY2xlcmsuYWNjb3VudHMuZGV2JA
Description
Environment
| Component | Version |
|---|---|
@clerk/expo |
3.1.6 |
@clerk/clerk-js |
6.4.0 (resolved to clerk.native.js) |
@clerk/react |
6.1.4 |
@clerk/shared |
4.4.0 |
| Expo SDK | 55 |
| React Native | 0.83.2 |
| React | 19.2.0 |
| iOS (physical device) | 26 |
| iOS Simulator | 26.4 (iPhone 17 Pro) |
| macOS | Darwin 25.2.0 |
| Package manager | pnpm (monorepo) |
@clerk/expo is in the app.json plugins array. Build output confirms ✅ Clerk iOS plugin loaded.
Problem
After migrating from @clerk/clerk-expo v2 to @clerk/expo v3, useAuth().isLoaded stays false forever. This happens on both iOS Simulator and a physical device.
clerk.load() is called but the returned promise never resolves or rejects — it hangs indefinitely.
What we verified
-
Correct native build loaded: Metro resolves
@clerk/clerk-jstodist/clerk.native.js(confirmed via customresolveRequestlogging). MD5 hash matches the working test app. -
Network works: Raw
fetch()to the Clerk FAPI (/v1/environment,/v1/client) returns200from inside the app — including with the same headers clerk-js adds (authorization,x-mobile,x-expo-sdk-version) and usingRequestobjects. -
clerk-js fetch calls hang: When intercepting
globalThis.fetch, we see clerk-js's FAPI requests being sent (→) but responses never arrive (←). The same URLs with the same headers and the same cached JWT return200when called manually. -
Not a stale token issue: Cleared
__clerk_client_jwtfrom SecureStore. Also tested fetch with the actual cached JWT — works fine. -
Minimal app works: A standalone Expo project (npm, same
@clerk/[email protected], same publishable key, same physical device) works correctly —isLoadedbecomestrue. -
Stripped app to bare minimum: Removed all app initialization code, store syncing, and hooks. Only
ClerkProvider→Stack→ screen withuseAuth(). Still stuck atisLoaded: false. -
All package versions identical between working test app and broken real app.
-
Clean reinstall: Deleted
ios/, allnode_modules/, reinstalled withpnpm install, and rebuilt withnpx expo run:ios. Same result.
Native SDK works, JS SDK does not
The native side of @clerk/expo works correctly. useNativeSession() returns the expected auth state, <AuthView /> renders and completes sign-in, and the native module (ClerkExpo.getSession(), ClerkExpo.getClientToken()) all function. Only the JS layer (clerk-js / useAuth()) fails to initialize.
Possibly related
#8236 (similar symptom but simulator-only)
Environment
System:
OS: macOS 26.2
CPU: (10) arm64 Apple M2 Pro
Memory: 386.73 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 24.13.0 - /Users/maximilian/.nvm/versions/node/v24.13.0/bin/node
npm: 11.6.2 - /Users/maximilian/.nvm/versions/node/v24.13.0/bin/npm
pnpm: 10.28.2 - /Users/maximilian/.nvm/versions/node/v24.13.0/bin/pnpm
Browsers:
Firefox: 147.0.3
Safari: 26.2
npmPackages:
@babel/plugin-proposal-decorators: ^7.28.0 => 7.29.0
@clerk/expo: ^3.1.6 => 3.1.6
@expo-google-fonts/inter: ^0.4.2 => 0.4.2
@expo/ui: ~55.0.5 => 55.0.5
@expo/vector-icons: ^15.0.3 => 15.0.3
@kingstinct/react-native-healthkit: ^13.1.0 => 13.1.1
@morrowdigital/watermelondb-expo-plugin: ^2.4.0-beta.0 => 2.4.0-beta.0
@nozbe/watermelondb: ^0.28.0 => 0.28.0
@react-native-async-storage/async-storage: ^2.2.0 => 2.2.0
@react-native-community/slider: ^5.1.1 => 5.1.2
@react-navigation/bottom-tabs: ^7.4.0 => 7.12.0
@react-navigation/elements: ^2.6.3 => 2.9.5
@react-navigation/native: ^7.1.8 => 7.1.28
@rnmapbox/maps: ^10.2.10 => 10.2.10
@sippd/shared: workspace:* => 1.0.0
@testing-library/react-native: ^13.3.3 => 13.3.3
@types/jest: ^29.5.14 => 29.5.14
@types/node: ^25.2.0 => 25.2.0
@types/react: ~19.2.14 => 19.2.14
eslint: ^9.25.0 => 9.39.2
eslint-config-expo: ~55.0.0 => 55.0.0
expo: ~55.0.8 => 55.0.8
expo-apple-authentication: ^55.0.8 => 55.0.8
expo-auth-session: ~55.0.12 => 55.0.12
expo-blur: ^55.0.8 => 55.0.8
expo-constants: ~55.0.7 => 55.0.7
expo-crypto: ^55.0.8 => 55.0.8
expo-dev-client: ~55.0.22 => 55.0.22
expo-font: ~55.0.4 => 55.0.4
expo-haptics: ~55.0.8 => 55.0.8
expo-image: ~55.0.5 => 55.0.5
expo-linking: ~55.0.7 => 55.0.7
expo-location: ~55.1.2 => 55.1.2
expo-router: ~55.0.3 => 55.0.3
expo-secure-store: ~55.0.11 => 55.0.11
expo-splash-screen: ~55.0.10 => 55.0.10
expo-status-bar: ~55.0.4 => 55.0.4
expo-symbols: ~55.0.4 => 55.0.4
expo-system-ui: ~55.0.9 => 55.0.9
expo-web-browser: ~55.0.12 => 55.0.12
jest: ^29.7.0 => 29.7.0
jest-expo: ^55.0.9 => 55.0.9
phosphor-react-native: ^3.0.3 => 3.0.3
react: 19.2.0 => 19.2.0
react-dom: 19.2.0 => 19.2.0
react-native: 0.83.2 => 0.83.2
react-native-gesture-handler: 3.0.0-beta.2 => 3.0.0-beta.2
react-native-nitro-modules: ^0.33.2 => 0.33.3
react-native-reanimated: ~4.2.1 => 4.2.1
react-native-safe-area-context: ~5.6.2 => 5.6.2
react-native-screens: ~4.23.0 => 4.23.0
react-native-svg: ^15.15.3 => 15.15.3
react-native-web: ~0.21.0 => 0.21.2
react-native-worklets: 0.7.2 => 0.7.2
typescript: ~5.9.2 => 5.9.3
zustand: ^5.0.9 => 5.0.11