Skip to content

fix(expo): prevent session loss on Expo JS reload - #8469

Merged
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload
May 4, 2026
Merged

fix(expo): prevent session loss on Expo JS reload#8469
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Description

Mirror of @souyahia's #8437 brought to the upstream repo so the integration tests (which are gated to non-fork PRs for secret access) can run. Same single-commit change, with original authorship preserved via cherry-pick.

Original PR: #8437
Original author: @souyahia (Samy Ouyahia samy.ouyahia@mistral.ai) — preserved as the commit author here.

Bug

NativeSessionSync calls native signOut() whenever isSignedIn is falsy, including the brief loading window where it's undefined. On a JS reload (pressing R in Expo, or Metro bundle change), JS state resets but the native module persists, so signOut() goes through and:

  • calls Clerk.shared.auth.signOut(sessionId:) → server-side session revoke
  • calls Clerk.clearAllKeychainItems() → wipes native keychain entries

When Clerk JS finishes loading on the new bundle, /v1/client returns no session and the user is back on the sign-in screen. Confirmed reproducible on both iOS and Android, and on both the native <AuthView /> flow and pure JS useSignIn() flow (since <NativeSessionSync /> is mounted unconditionally for isNative() apps in ClerkProvider).

Fix

Add an isLoaded guard so the native signOut() only runs when Clerk has fully loaded and confirmed the user is actually signed out — not during the loading phase where isSignedIn === undefined.

Test plan

Verified end-to-end against clerk-expo-quickstart/NativeComponentQuickstart:

SetupResult
Buggy @clerk/expo@3.1.8 (no guard), iOS sim, native <AuthView />1 reload → session lost ❌
Buggy 3.1.8, iOS sim, JS-only useSignIn flow1 reload → session lost ❌
This fix, iOS sim, native <AuthView />4 reloads → session persisted ✅
This fix, Android emulator, native <AuthView />3 reloads → session persisted ✅

NativeSessionSync was calling native signOut() during the loading phase
when isSignedIn is undefined (!undefined === true). On a JS reload
(pressing R in Expo), the native module persists from the previous
session, so signOut() goes through and revokes the session server-side
via Clerk.shared.auth.signOut() and clears all keychain items via
Clerk.clearAllKeychainItems(), forcing the user to log in again.
On a full app restart (kill + reopen), the native module factory is
uninitialized (process died), so signOut() rejects with
E_NOT_INITIALIZED and the session is preserved.
This adds an isLoaded guard so native signOut() is only called when
Clerk has fully loaded and confirmed the user is actually signed out,
not during the initial loading phase.
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 4, 2026 6:55pm

Request Review

@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bddc8b7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@clerk/expoPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 454d8782-3b79-479d-af34-3da6f2a4fddc

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea8a0b and bddc8b7.

📒 Files selected for processing (2)
  • .changeset/fix-native-session-sync-reload.md
  • packages/expo/src/provider/ClerkProvider.tsx

📝 Walkthrough

Walkthrough

This PR fixes a session loss issue that occurs on Expo JS reloads (e.g., pressing R in dev). It adds an isLoaded guard to the NativeSessionSync component in ClerkProvider to prevent the native signOut() call from executing during the auth loading phase when isSignedIn is undefined. Without this guard, the native module's stale session reference gets cleared during reload, revoking the server-side session and forcing re-login.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): prevent session loss on Expo JS reload' directly and clearly summarizes the main change—adding an isLoaded guard to prevent unwanted session sign-out during the loading phase.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the bug, the fix approach, and end-to-end testing results on iOS and Android.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@chriscaninchriscanin self-assigned this May 4, 2026
@pkg-pr-new

pkg-pr-newBot commented May 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8469

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8469

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8469

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8469

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8469

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8469

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8469

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8469

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8469

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8469

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8469

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8469

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8469

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8469

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8469

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8469

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8469

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8469

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8469

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8469

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8469

commit: bddc8b7

@wobsorianowobsoriano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good here

@jacekradko
jacekradko merged commit 5b4c574 into mainMay 4, 2026
72 of 74 checks passed
@jacekradko
jacekradko deleted the chris/fix-expo-native-session-sync-reload branch May 4, 2026 23:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@chriscanin@wobsoriano@jacekradko@souyahia
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(expo): prevent session loss on Expo JS reload by chriscanin · Pull Request #8469 · clerk/javascript · GitHub
Skip to content

fix(expo): prevent session loss on Expo JS reload - #8469

Merged
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload
May 4, 2026
Merged

fix(expo): prevent session loss on Expo JS reload#8469
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Description

Mirror of @souyahia's #8437 brought to the upstream repo so the integration tests (which are gated to non-fork PRs for secret access) can run. Same single-commit change, with original authorship preserved via cherry-pick.

Original PR: #8437
Original author: @souyahia (Samy Ouyahia samy.ouyahia@mistral.ai) — preserved as the commit author here.

Bug

NativeSessionSync calls native signOut() whenever isSignedIn is falsy, including the brief loading window where it's undefined. On a JS reload (pressing R in Expo, or Metro bundle change), JS state resets but the native module persists, so signOut() goes through and:

  • calls Clerk.shared.auth.signOut(sessionId:) → server-side session revoke
  • calls Clerk.clearAllKeychainItems() → wipes native keychain entries

When Clerk JS finishes loading on the new bundle, /v1/client returns no session and the user is back on the sign-in screen. Confirmed reproducible on both iOS and Android, and on both the native <AuthView /> flow and pure JS useSignIn() flow (since <NativeSessionSync /> is mounted unconditionally for isNative() apps in ClerkProvider).

Fix

Add an isLoaded guard so the native signOut() only runs when Clerk has fully loaded and confirmed the user is actually signed out — not during the loading phase where isSignedIn === undefined.

Test plan

Verified end-to-end against clerk-expo-quickstart/NativeComponentQuickstart:

SetupResult
Buggy @clerk/expo@3.1.8 (no guard), iOS sim, native <AuthView />1 reload → session lost ❌
Buggy 3.1.8, iOS sim, JS-only useSignIn flow1 reload → session lost ❌
This fix, iOS sim, native <AuthView />4 reloads → session persisted ✅
This fix, Android emulator, native <AuthView />3 reloads → session persisted ✅

NativeSessionSync was calling native signOut() during the loading phase
when isSignedIn is undefined (!undefined === true). On a JS reload
(pressing R in Expo), the native module persists from the previous
session, so signOut() goes through and revokes the session server-side
via Clerk.shared.auth.signOut() and clears all keychain items via
Clerk.clearAllKeychainItems(), forcing the user to log in again.
On a full app restart (kill + reopen), the native module factory is
uninitialized (process died), so signOut() rejects with
E_NOT_INITIALIZED and the session is preserved.
This adds an isLoaded guard so native signOut() is only called when
Clerk has fully loaded and confirmed the user is actually signed out,
not during the initial loading phase.
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 4, 2026 6:55pm

Request Review

@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bddc8b7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@clerk/expoPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 454d8782-3b79-479d-af34-3da6f2a4fddc

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea8a0b and bddc8b7.

📒 Files selected for processing (2)
  • .changeset/fix-native-session-sync-reload.md
  • packages/expo/src/provider/ClerkProvider.tsx

📝 Walkthrough

Walkthrough

This PR fixes a session loss issue that occurs on Expo JS reloads (e.g., pressing R in dev). It adds an isLoaded guard to the NativeSessionSync component in ClerkProvider to prevent the native signOut() call from executing during the auth loading phase when isSignedIn is undefined. Without this guard, the native module's stale session reference gets cleared during reload, revoking the server-side session and forcing re-login.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): prevent session loss on Expo JS reload' directly and clearly summarizes the main change—adding an isLoaded guard to prevent unwanted session sign-out during the loading phase.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the bug, the fix approach, and end-to-end testing results on iOS and Android.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@chriscaninchriscanin self-assigned this May 4, 2026
@pkg-pr-new

pkg-pr-newBot commented May 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8469

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8469

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8469

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8469

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8469

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8469

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8469

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8469

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8469

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8469

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8469

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8469

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8469

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8469

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8469

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8469

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8469

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8469

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8469

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8469

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8469

commit: bddc8b7

@wobsorianowobsoriano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good here

@jacekradko
jacekradko merged commit 5b4c574 into mainMay 4, 2026
72 of 74 checks passed
@jacekradko
jacekradko deleted the chris/fix-expo-native-session-sync-reload branch May 4, 2026 23:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@chriscanin@wobsoriano@jacekradko@souyahia
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(expo): prevent session loss on Expo JS reload by chriscanin · Pull Request #8469 · clerk/javascript · GitHub
Skip to content

fix(expo): prevent session loss on Expo JS reload - #8469

Merged
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload
May 4, 2026
Merged

fix(expo): prevent session loss on Expo JS reload#8469
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Description

Mirror of @souyahia's #8437 brought to the upstream repo so the integration tests (which are gated to non-fork PRs for secret access) can run. Same single-commit change, with original authorship preserved via cherry-pick.

Original PR: #8437
Original author: @souyahia (Samy Ouyahia samy.ouyahia@mistral.ai) — preserved as the commit author here.

Bug

NativeSessionSync calls native signOut() whenever isSignedIn is falsy, including the brief loading window where it's undefined. On a JS reload (pressing R in Expo, or Metro bundle change), JS state resets but the native module persists, so signOut() goes through and:

  • calls Clerk.shared.auth.signOut(sessionId:) → server-side session revoke
  • calls Clerk.clearAllKeychainItems() → wipes native keychain entries

When Clerk JS finishes loading on the new bundle, /v1/client returns no session and the user is back on the sign-in screen. Confirmed reproducible on both iOS and Android, and on both the native <AuthView /> flow and pure JS useSignIn() flow (since <NativeSessionSync /> is mounted unconditionally for isNative() apps in ClerkProvider).

Fix

Add an isLoaded guard so the native signOut() only runs when Clerk has fully loaded and confirmed the user is actually signed out — not during the loading phase where isSignedIn === undefined.

Test plan

Verified end-to-end against clerk-expo-quickstart/NativeComponentQuickstart:

SetupResult
Buggy @clerk/expo@3.1.8 (no guard), iOS sim, native <AuthView />1 reload → session lost ❌
Buggy 3.1.8, iOS sim, JS-only useSignIn flow1 reload → session lost ❌
This fix, iOS sim, native <AuthView />4 reloads → session persisted ✅
This fix, Android emulator, native <AuthView />3 reloads → session persisted ✅

NativeSessionSync was calling native signOut() during the loading phase
when isSignedIn is undefined (!undefined === true). On a JS reload
(pressing R in Expo), the native module persists from the previous
session, so signOut() goes through and revokes the session server-side
via Clerk.shared.auth.signOut() and clears all keychain items via
Clerk.clearAllKeychainItems(), forcing the user to log in again.
On a full app restart (kill + reopen), the native module factory is
uninitialized (process died), so signOut() rejects with
E_NOT_INITIALIZED and the session is preserved.
This adds an isLoaded guard so native signOut() is only called when
Clerk has fully loaded and confirmed the user is actually signed out,
not during the initial loading phase.
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 4, 2026 6:55pm

Request Review

@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bddc8b7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@clerk/expoPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 454d8782-3b79-479d-af34-3da6f2a4fddc

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea8a0b and bddc8b7.

📒 Files selected for processing (2)
  • .changeset/fix-native-session-sync-reload.md
  • packages/expo/src/provider/ClerkProvider.tsx

📝 Walkthrough

Walkthrough

This PR fixes a session loss issue that occurs on Expo JS reloads (e.g., pressing R in dev). It adds an isLoaded guard to the NativeSessionSync component in ClerkProvider to prevent the native signOut() call from executing during the auth loading phase when isSignedIn is undefined. Without this guard, the native module's stale session reference gets cleared during reload, revoking the server-side session and forcing re-login.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): prevent session loss on Expo JS reload' directly and clearly summarizes the main change—adding an isLoaded guard to prevent unwanted session sign-out during the loading phase.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the bug, the fix approach, and end-to-end testing results on iOS and Android.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@chriscaninchriscanin self-assigned this May 4, 2026
@pkg-pr-new

pkg-pr-newBot commented May 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8469

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8469

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8469

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8469

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8469

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8469

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8469

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8469

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8469

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8469

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8469

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8469

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8469

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8469

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8469

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8469

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8469

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8469

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8469

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8469

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8469

commit: bddc8b7

@wobsorianowobsoriano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good here

@jacekradko
jacekradko merged commit 5b4c574 into mainMay 4, 2026
72 of 74 checks passed
@jacekradko
jacekradko deleted the chris/fix-expo-native-session-sync-reload branch May 4, 2026 23:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@chriscanin@wobsoriano@jacekradko@souyahia
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(expo): prevent session loss on Expo JS reload by chriscanin · Pull Request #8469 · clerk/javascript · GitHub
Skip to content

fix(expo): prevent session loss on Expo JS reload - #8469

Merged
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload
May 4, 2026
Merged

fix(expo): prevent session loss on Expo JS reload#8469
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Description

Mirror of @souyahia's #8437 brought to the upstream repo so the integration tests (which are gated to non-fork PRs for secret access) can run. Same single-commit change, with original authorship preserved via cherry-pick.

Original PR: #8437
Original author: @souyahia (Samy Ouyahia samy.ouyahia@mistral.ai) — preserved as the commit author here.

Bug

NativeSessionSync calls native signOut() whenever isSignedIn is falsy, including the brief loading window where it's undefined. On a JS reload (pressing R in Expo, or Metro bundle change), JS state resets but the native module persists, so signOut() goes through and:

  • calls Clerk.shared.auth.signOut(sessionId:) → server-side session revoke
  • calls Clerk.clearAllKeychainItems() → wipes native keychain entries

When Clerk JS finishes loading on the new bundle, /v1/client returns no session and the user is back on the sign-in screen. Confirmed reproducible on both iOS and Android, and on both the native <AuthView /> flow and pure JS useSignIn() flow (since <NativeSessionSync /> is mounted unconditionally for isNative() apps in ClerkProvider).

Fix

Add an isLoaded guard so the native signOut() only runs when Clerk has fully loaded and confirmed the user is actually signed out — not during the loading phase where isSignedIn === undefined.

Test plan

Verified end-to-end against clerk-expo-quickstart/NativeComponentQuickstart:

SetupResult
Buggy @clerk/expo@3.1.8 (no guard), iOS sim, native <AuthView />1 reload → session lost ❌
Buggy 3.1.8, iOS sim, JS-only useSignIn flow1 reload → session lost ❌
This fix, iOS sim, native <AuthView />4 reloads → session persisted ✅
This fix, Android emulator, native <AuthView />3 reloads → session persisted ✅

NativeSessionSync was calling native signOut() during the loading phase
when isSignedIn is undefined (!undefined === true). On a JS reload
(pressing R in Expo), the native module persists from the previous
session, so signOut() goes through and revokes the session server-side
via Clerk.shared.auth.signOut() and clears all keychain items via
Clerk.clearAllKeychainItems(), forcing the user to log in again.
On a full app restart (kill + reopen), the native module factory is
uninitialized (process died), so signOut() rejects with
E_NOT_INITIALIZED and the session is preserved.
This adds an isLoaded guard so native signOut() is only called when
Clerk has fully loaded and confirmed the user is actually signed out,
not during the initial loading phase.
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 4, 2026 6:55pm

Request Review

@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bddc8b7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@clerk/expoPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 454d8782-3b79-479d-af34-3da6f2a4fddc

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea8a0b and bddc8b7.

📒 Files selected for processing (2)
  • .changeset/fix-native-session-sync-reload.md
  • packages/expo/src/provider/ClerkProvider.tsx

📝 Walkthrough

Walkthrough

This PR fixes a session loss issue that occurs on Expo JS reloads (e.g., pressing R in dev). It adds an isLoaded guard to the NativeSessionSync component in ClerkProvider to prevent the native signOut() call from executing during the auth loading phase when isSignedIn is undefined. Without this guard, the native module's stale session reference gets cleared during reload, revoking the server-side session and forcing re-login.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): prevent session loss on Expo JS reload' directly and clearly summarizes the main change—adding an isLoaded guard to prevent unwanted session sign-out during the loading phase.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the bug, the fix approach, and end-to-end testing results on iOS and Android.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@chriscaninchriscanin self-assigned this May 4, 2026
@pkg-pr-new

pkg-pr-newBot commented May 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8469

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8469

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8469

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8469

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8469

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8469

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8469

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8469

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8469

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8469

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8469

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8469

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8469

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8469

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8469

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8469

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8469

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8469

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8469

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8469

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8469

commit: bddc8b7

@wobsorianowobsoriano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good here

@jacekradko
jacekradko merged commit 5b4c574 into mainMay 4, 2026
72 of 74 checks passed
@jacekradko
jacekradko deleted the chris/fix-expo-native-session-sync-reload branch May 4, 2026 23:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@chriscanin@wobsoriano@jacekradko@souyahia
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(expo): prevent session loss on Expo JS reload by chriscanin · Pull Request #8469 · clerk/javascript · GitHub
Skip to content

fix(expo): prevent session loss on Expo JS reload - #8469

Merged
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload
May 4, 2026
Merged

fix(expo): prevent session loss on Expo JS reload#8469
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Description

Mirror of @souyahia's #8437 brought to the upstream repo so the integration tests (which are gated to non-fork PRs for secret access) can run. Same single-commit change, with original authorship preserved via cherry-pick.

Original PR: #8437
Original author: @souyahia (Samy Ouyahia samy.ouyahia@mistral.ai) — preserved as the commit author here.

Bug

NativeSessionSync calls native signOut() whenever isSignedIn is falsy, including the brief loading window where it's undefined. On a JS reload (pressing R in Expo, or Metro bundle change), JS state resets but the native module persists, so signOut() goes through and:

  • calls Clerk.shared.auth.signOut(sessionId:) → server-side session revoke
  • calls Clerk.clearAllKeychainItems() → wipes native keychain entries

When Clerk JS finishes loading on the new bundle, /v1/client returns no session and the user is back on the sign-in screen. Confirmed reproducible on both iOS and Android, and on both the native <AuthView /> flow and pure JS useSignIn() flow (since <NativeSessionSync /> is mounted unconditionally for isNative() apps in ClerkProvider).

Fix

Add an isLoaded guard so the native signOut() only runs when Clerk has fully loaded and confirmed the user is actually signed out — not during the loading phase where isSignedIn === undefined.

Test plan

Verified end-to-end against clerk-expo-quickstart/NativeComponentQuickstart:

SetupResult
Buggy @clerk/expo@3.1.8 (no guard), iOS sim, native <AuthView />1 reload → session lost ❌
Buggy 3.1.8, iOS sim, JS-only useSignIn flow1 reload → session lost ❌
This fix, iOS sim, native <AuthView />4 reloads → session persisted ✅
This fix, Android emulator, native <AuthView />3 reloads → session persisted ✅

NativeSessionSync was calling native signOut() during the loading phase
when isSignedIn is undefined (!undefined === true). On a JS reload
(pressing R in Expo), the native module persists from the previous
session, so signOut() goes through and revokes the session server-side
via Clerk.shared.auth.signOut() and clears all keychain items via
Clerk.clearAllKeychainItems(), forcing the user to log in again.
On a full app restart (kill + reopen), the native module factory is
uninitialized (process died), so signOut() rejects with
E_NOT_INITIALIZED and the session is preserved.
This adds an isLoaded guard so native signOut() is only called when
Clerk has fully loaded and confirmed the user is actually signed out,
not during the initial loading phase.
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 4, 2026 6:55pm

Request Review

@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bddc8b7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@clerk/expoPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 454d8782-3b79-479d-af34-3da6f2a4fddc

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea8a0b and bddc8b7.

📒 Files selected for processing (2)
  • .changeset/fix-native-session-sync-reload.md
  • packages/expo/src/provider/ClerkProvider.tsx

📝 Walkthrough

Walkthrough

This PR fixes a session loss issue that occurs on Expo JS reloads (e.g., pressing R in dev). It adds an isLoaded guard to the NativeSessionSync component in ClerkProvider to prevent the native signOut() call from executing during the auth loading phase when isSignedIn is undefined. Without this guard, the native module's stale session reference gets cleared during reload, revoking the server-side session and forcing re-login.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): prevent session loss on Expo JS reload' directly and clearly summarizes the main change—adding an isLoaded guard to prevent unwanted session sign-out during the loading phase.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the bug, the fix approach, and end-to-end testing results on iOS and Android.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@chriscaninchriscanin self-assigned this May 4, 2026
@pkg-pr-new

pkg-pr-newBot commented May 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8469

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8469

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8469

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8469

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8469

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8469

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8469

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8469

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8469

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8469

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8469

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8469

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8469

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8469

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8469

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8469

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8469

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8469

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8469

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8469

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8469

commit: bddc8b7

@wobsorianowobsoriano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good here

@jacekradko
jacekradko merged commit 5b4c574 into mainMay 4, 2026
72 of 74 checks passed
@jacekradko
jacekradko deleted the chris/fix-expo-native-session-sync-reload branch May 4, 2026 23:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@chriscanin@wobsoriano@jacekradko@souyahia
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(expo): prevent session loss on Expo JS reload by chriscanin · Pull Request #8469 · clerk/javascript · GitHub
Skip to content

fix(expo): prevent session loss on Expo JS reload - #8469

Merged
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload
May 4, 2026
Merged

fix(expo): prevent session loss on Expo JS reload#8469
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Description

Mirror of @souyahia's #8437 brought to the upstream repo so the integration tests (which are gated to non-fork PRs for secret access) can run. Same single-commit change, with original authorship preserved via cherry-pick.

Original PR: #8437
Original author: @souyahia (Samy Ouyahia samy.ouyahia@mistral.ai) — preserved as the commit author here.

Bug

NativeSessionSync calls native signOut() whenever isSignedIn is falsy, including the brief loading window where it's undefined. On a JS reload (pressing R in Expo, or Metro bundle change), JS state resets but the native module persists, so signOut() goes through and:

  • calls Clerk.shared.auth.signOut(sessionId:) → server-side session revoke
  • calls Clerk.clearAllKeychainItems() → wipes native keychain entries

When Clerk JS finishes loading on the new bundle, /v1/client returns no session and the user is back on the sign-in screen. Confirmed reproducible on both iOS and Android, and on both the native <AuthView /> flow and pure JS useSignIn() flow (since <NativeSessionSync /> is mounted unconditionally for isNative() apps in ClerkProvider).

Fix

Add an isLoaded guard so the native signOut() only runs when Clerk has fully loaded and confirmed the user is actually signed out — not during the loading phase where isSignedIn === undefined.

Test plan

Verified end-to-end against clerk-expo-quickstart/NativeComponentQuickstart:

SetupResult
Buggy @clerk/expo@3.1.8 (no guard), iOS sim, native <AuthView />1 reload → session lost ❌
Buggy 3.1.8, iOS sim, JS-only useSignIn flow1 reload → session lost ❌
This fix, iOS sim, native <AuthView />4 reloads → session persisted ✅
This fix, Android emulator, native <AuthView />3 reloads → session persisted ✅

NativeSessionSync was calling native signOut() during the loading phase
when isSignedIn is undefined (!undefined === true). On a JS reload
(pressing R in Expo), the native module persists from the previous
session, so signOut() goes through and revokes the session server-side
via Clerk.shared.auth.signOut() and clears all keychain items via
Clerk.clearAllKeychainItems(), forcing the user to log in again.
On a full app restart (kill + reopen), the native module factory is
uninitialized (process died), so signOut() rejects with
E_NOT_INITIALIZED and the session is preserved.
This adds an isLoaded guard so native signOut() is only called when
Clerk has fully loaded and confirmed the user is actually signed out,
not during the initial loading phase.
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 4, 2026 6:55pm

Request Review

@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bddc8b7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@clerk/expoPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 454d8782-3b79-479d-af34-3da6f2a4fddc

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea8a0b and bddc8b7.

📒 Files selected for processing (2)
  • .changeset/fix-native-session-sync-reload.md
  • packages/expo/src/provider/ClerkProvider.tsx

📝 Walkthrough

Walkthrough

This PR fixes a session loss issue that occurs on Expo JS reloads (e.g., pressing R in dev). It adds an isLoaded guard to the NativeSessionSync component in ClerkProvider to prevent the native signOut() call from executing during the auth loading phase when isSignedIn is undefined. Without this guard, the native module's stale session reference gets cleared during reload, revoking the server-side session and forcing re-login.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): prevent session loss on Expo JS reload' directly and clearly summarizes the main change—adding an isLoaded guard to prevent unwanted session sign-out during the loading phase.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the bug, the fix approach, and end-to-end testing results on iOS and Android.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@chriscaninchriscanin self-assigned this May 4, 2026
@pkg-pr-new

pkg-pr-newBot commented May 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8469

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8469

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8469

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8469

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8469

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8469

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8469

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8469

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8469

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8469

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8469

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8469

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8469

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8469

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8469

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8469

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8469

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8469

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8469

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8469

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8469

commit: bddc8b7

@wobsorianowobsoriano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good here

@jacekradko
jacekradko merged commit 5b4c574 into mainMay 4, 2026
72 of 74 checks passed
@jacekradko
jacekradko deleted the chris/fix-expo-native-session-sync-reload branch May 4, 2026 23:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@chriscanin@wobsoriano@jacekradko@souyahia
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(expo): prevent session loss on Expo JS reload by chriscanin · Pull Request #8469 · clerk/javascript · GitHub
Skip to content

fix(expo): prevent session loss on Expo JS reload - #8469

Merged
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload
May 4, 2026
Merged

fix(expo): prevent session loss on Expo JS reload#8469
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Description

Mirror of @souyahia's #8437 brought to the upstream repo so the integration tests (which are gated to non-fork PRs for secret access) can run. Same single-commit change, with original authorship preserved via cherry-pick.

Original PR: #8437
Original author: @souyahia (Samy Ouyahia samy.ouyahia@mistral.ai) — preserved as the commit author here.

Bug

NativeSessionSync calls native signOut() whenever isSignedIn is falsy, including the brief loading window where it's undefined. On a JS reload (pressing R in Expo, or Metro bundle change), JS state resets but the native module persists, so signOut() goes through and:

  • calls Clerk.shared.auth.signOut(sessionId:) → server-side session revoke
  • calls Clerk.clearAllKeychainItems() → wipes native keychain entries

When Clerk JS finishes loading on the new bundle, /v1/client returns no session and the user is back on the sign-in screen. Confirmed reproducible on both iOS and Android, and on both the native <AuthView /> flow and pure JS useSignIn() flow (since <NativeSessionSync /> is mounted unconditionally for isNative() apps in ClerkProvider).

Fix

Add an isLoaded guard so the native signOut() only runs when Clerk has fully loaded and confirmed the user is actually signed out — not during the loading phase where isSignedIn === undefined.

Test plan

Verified end-to-end against clerk-expo-quickstart/NativeComponentQuickstart:

SetupResult
Buggy @clerk/expo@3.1.8 (no guard), iOS sim, native <AuthView />1 reload → session lost ❌
Buggy 3.1.8, iOS sim, JS-only useSignIn flow1 reload → session lost ❌
This fix, iOS sim, native <AuthView />4 reloads → session persisted ✅
This fix, Android emulator, native <AuthView />3 reloads → session persisted ✅

NativeSessionSync was calling native signOut() during the loading phase
when isSignedIn is undefined (!undefined === true). On a JS reload
(pressing R in Expo), the native module persists from the previous
session, so signOut() goes through and revokes the session server-side
via Clerk.shared.auth.signOut() and clears all keychain items via
Clerk.clearAllKeychainItems(), forcing the user to log in again.
On a full app restart (kill + reopen), the native module factory is
uninitialized (process died), so signOut() rejects with
E_NOT_INITIALIZED and the session is preserved.
This adds an isLoaded guard so native signOut() is only called when
Clerk has fully loaded and confirmed the user is actually signed out,
not during the initial loading phase.
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 4, 2026 6:55pm

Request Review

@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bddc8b7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@clerk/expoPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 454d8782-3b79-479d-af34-3da6f2a4fddc

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea8a0b and bddc8b7.

📒 Files selected for processing (2)
  • .changeset/fix-native-session-sync-reload.md
  • packages/expo/src/provider/ClerkProvider.tsx

📝 Walkthrough

Walkthrough

This PR fixes a session loss issue that occurs on Expo JS reloads (e.g., pressing R in dev). It adds an isLoaded guard to the NativeSessionSync component in ClerkProvider to prevent the native signOut() call from executing during the auth loading phase when isSignedIn is undefined. Without this guard, the native module's stale session reference gets cleared during reload, revoking the server-side session and forcing re-login.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): prevent session loss on Expo JS reload' directly and clearly summarizes the main change—adding an isLoaded guard to prevent unwanted session sign-out during the loading phase.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the bug, the fix approach, and end-to-end testing results on iOS and Android.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@chriscaninchriscanin self-assigned this May 4, 2026
@pkg-pr-new

pkg-pr-newBot commented May 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8469

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8469

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8469

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8469

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8469

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8469

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8469

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8469

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8469

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8469

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8469

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8469

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8469

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8469

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8469

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8469

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8469

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8469

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8469

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8469

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8469

commit: bddc8b7

@wobsorianowobsoriano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good here

@jacekradko
jacekradko merged commit 5b4c574 into mainMay 4, 2026
72 of 74 checks passed
@jacekradko
jacekradko deleted the chris/fix-expo-native-session-sync-reload branch May 4, 2026 23:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@chriscanin@wobsoriano@jacekradko@souyahia
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix(expo): prevent session loss on Expo JS reload by chriscanin · Pull Request #8469 · clerk/javascript · GitHub
Skip to content

fix(expo): prevent session loss on Expo JS reload - #8469

Merged
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload
May 4, 2026
Merged

fix(expo): prevent session loss on Expo JS reload#8469
jacekradko merged 1 commit into
mainfrom
chris/fix-expo-native-session-sync-reload

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Description

Mirror of @souyahia's #8437 brought to the upstream repo so the integration tests (which are gated to non-fork PRs for secret access) can run. Same single-commit change, with original authorship preserved via cherry-pick.

Original PR: #8437
Original author: @souyahia (Samy Ouyahia samy.ouyahia@mistral.ai) — preserved as the commit author here.

Bug

NativeSessionSync calls native signOut() whenever isSignedIn is falsy, including the brief loading window where it's undefined. On a JS reload (pressing R in Expo, or Metro bundle change), JS state resets but the native module persists, so signOut() goes through and:

  • calls Clerk.shared.auth.signOut(sessionId:) → server-side session revoke
  • calls Clerk.clearAllKeychainItems() → wipes native keychain entries

When Clerk JS finishes loading on the new bundle, /v1/client returns no session and the user is back on the sign-in screen. Confirmed reproducible on both iOS and Android, and on both the native <AuthView /> flow and pure JS useSignIn() flow (since <NativeSessionSync /> is mounted unconditionally for isNative() apps in ClerkProvider).

Fix

Add an isLoaded guard so the native signOut() only runs when Clerk has fully loaded and confirmed the user is actually signed out — not during the loading phase where isSignedIn === undefined.

Test plan

Verified end-to-end against clerk-expo-quickstart/NativeComponentQuickstart:

SetupResult
Buggy @clerk/expo@3.1.8 (no guard), iOS sim, native <AuthView />1 reload → session lost ❌
Buggy 3.1.8, iOS sim, JS-only useSignIn flow1 reload → session lost ❌
This fix, iOS sim, native <AuthView />4 reloads → session persisted ✅
This fix, Android emulator, native <AuthView />3 reloads → session persisted ✅

NativeSessionSync was calling native signOut() during the loading phase
when isSignedIn is undefined (!undefined === true). On a JS reload
(pressing R in Expo), the native module persists from the previous
session, so signOut() goes through and revokes the session server-side
via Clerk.shared.auth.signOut() and clears all keychain items via
Clerk.clearAllKeychainItems(), forcing the user to log in again.
On a full app restart (kill + reopen), the native module factory is
uninitialized (process died), so signOut() rejects with
E_NOT_INITIALIZED and the session is preserved.
This adds an isLoaded guard so native signOut() is only called when
Clerk has fully loaded and confirmed the user is actually signed out,
not during the initial loading phase.
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 4, 2026 6:55pm

Request Review

@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bddc8b7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@clerk/expoPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 454d8782-3b79-479d-af34-3da6f2a4fddc

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea8a0b and bddc8b7.

📒 Files selected for processing (2)
  • .changeset/fix-native-session-sync-reload.md
  • packages/expo/src/provider/ClerkProvider.tsx

📝 Walkthrough

Walkthrough

This PR fixes a session loss issue that occurs on Expo JS reloads (e.g., pressing R in dev). It adds an isLoaded guard to the NativeSessionSync component in ClerkProvider to prevent the native signOut() call from executing during the auth loading phase when isSignedIn is undefined. Without this guard, the native module's stale session reference gets cleared during reload, revoking the server-side session and forcing re-login.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): prevent session loss on Expo JS reload' directly and clearly summarizes the main change—adding an isLoaded guard to prevent unwanted session sign-out during the loading phase.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the bug, the fix approach, and end-to-end testing results on iOS and Android.
Docstring Coverage✅ PassedDocstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@chriscaninchriscanin self-assigned this May 4, 2026
@pkg-pr-new

pkg-pr-newBot commented May 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8469

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8469

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8469

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8469

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8469

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8469

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8469

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8469

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8469

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8469

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8469

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8469

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8469

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8469

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8469

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8469

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8469

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8469

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8469

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8469

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8469

commit: bddc8b7

@wobsorianowobsoriano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good here

@jacekradko
jacekradko merged commit 5b4c574 into mainMay 4, 2026
72 of 74 checks passed
@jacekradko
jacekradko deleted the chris/fix-expo-native-session-sync-reload branch May 4, 2026 23:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@chriscanin@wobsoriano@jacekradko@souyahia