Skip to content

@clerk/expo v3: Session not restored on Metro JS reload on Android despite valid JWT in tokenCache #8265

@prilepskiy

Description

@prilepskiy

Preliminary Checks

Reproduction

https://github.com/clerk/clerk-expo-quickstart

Publishable key

pk_test_YmlnLWZpbGx5LTM3LmNsZXJrLmFjY291bnRzLmRldiQ

Description

After migrating from @clerk/clerk-expo v2 to @clerk/expo v3.1.6, Metro JS reload (pressing R) causes session loss despite valid JWT in tokenCache.

This did NOT happen with @clerk/clerk-expo v2.

Steps to reproduce:

  1. Set up @clerk/expo v3 with tokenCache from @clerk/expo/token-cache
  2. Add @clerk/expo to plugins in app.config.js
  3. Run npx expo prebuild --clean, then npx expo run:ios or npx expo run:android
  4. Sign in successfully
  5. Press R in Metro terminal (JS reload)
  6. User is redirected to sign-in screen — session lost

Expected behavior:

Session should be restored from cached JWT, same as v2 and same as app kill + reopen.

Actual behavior:

tokenCache.getToken("__clerk_client_jwt") returns a valid JWT, but useAuth() resolves with isLoaded: true, isSignedIn: false, userId: null.

App kill + reopen works correctly — session persists. The issue is only with JS-only reload where native context survives but JS re-initializes.

Logs after sign-in:

getToken __clerk_client_jwt eyJhbGciOiJSUzI1NiIs...
useAuth: isLoaded=true, isSignedIn=true, userId=user_xxx

Logs after Metro JS reload:

getToken __clerk_client_jwt eyJhbGciOiJSUzI1NiIs... ← same valid JWT
useAuth: isLoaded=true, isSignedIn=false, userId=null ← session lost

Logs after app kill + reopen:

getToken __clerk_client_jwt eyJhbGciOiJSUzI1NiIs...
useAuth: isLoaded=true, isSignedIn=true, userId=user_xxx ← works fine

What I've tried:

  • Custom tokenCache with logging — JWT is present and read correctly
  • Built-in @clerk/expo/token-cache — same result
  • @clerk/expo added to plugins in app.config.js (native module confirmed: "Clerk iOS plugin loaded", "Clerk Android plugin loaded")
  • publishableKey via process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY (inlined by Metro)
  • useAuth({ treatPendingAsSignedOut: false }) — isSignedIn still resolves to false
  • npx expo prebuild --clean + full native rebuild with --no-build-cache
  • Tested on Android physical device and emulator — session lost on JS reload
  • Tested on iOS physical device — session survives JS reload correctly
  • App kill + reopen works fine on both platforms

Possibly related: #8245, #8236

Environment

System:
    OS: macOS 26.0
    CPU: (8) arm64 Apple M1 Pro
    Memory: 89.36 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 23.3.0 - /Users/prilepskiy/.asdf/installs/nodejs/23.3.0/bin/node
    npm: 10.9.0 - /Users/prilepskiy/.asdf/plugins/nodejs/shims/npm
    bun: 1.3.6 - /Users/prilepskiy/.bun/bin/bun
  npmPackages:
    @clerk/expo: ^3.1.6 => 3.1.6
    expo: ~54.0.33 => 54.0.33
    react: 19.1.0 => 19.1.0
    react-native: 0.81.5 => 0.81.5

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageA ticket that needs to be triaged by a team member

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions