Skip to content

fix(expo): preserve token cache method context - #8713

Merged
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue
Jun 1, 2026
Merged

fix(expo): preserve token cache method context#8713
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue

Conversation

@mikepitre

@mikepitremikepitre commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Preserve tokenCache method context when @clerk/expo wires the native Clerk singleton into clerk-js request hooks.
  • Add regression coverage for a class-instance token cache whose methods rely on this.
  • Add a patch changeset for @clerk/expo.
  • Plain Ticket

This was an issue with some updates internally in the clerk sdk, with how it uses the tokenCache. The previous recommended code for the tokenCache no longer worked as clerk destructured the getValue and setValue functions from the tokenCache internally. This broke the cache when implemented as a class instance and not a pure object as the functions are not bound to the class instance when they rely on instance properties from the tokenCache class.

It's worth noting that anyone using a standard setup with the recomended code from (older) clerk documentation prior to clerk exporting a tokenCache from ckerk itself will have a high chance to hit this issue, so I suggest changing the clerk react-native sdk to be backwards compatible with the older tokenCache class instance.

Customer Context

A customer upgrading to Expo 56 and the latest @clerk/expo reported that the app stayed stuck loading forever on <ClerkLoaded>, and calls such as useAuth().getToken() did not resolve on iOS Simulator 26.3.

They later narrowed it down to their older documented tokenCache implementation. Their cache was implemented as a class instance, and the cache methods depended on instance properties. Because @clerk/expo detached getToken and saveToken from the provided tokenCache, those methods lost their receiver and no longer had access to this.

This keeps both cache styles working.

Supported Token Cache Styles

Plain object cache:

consttokenCache={asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(key);},asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(key,token);},};<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Class instance cache:

classTokenCache{privateprefix='clerk:';asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(this.prefix+key);}asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(this.prefix+key,token);}}consttokenCache=newTokenCache();<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Testing

  • pnpm --dir packages/expo exec vitest run src/provider/singleton/__tests__/createClerkInstance.test.ts
  • pnpm --dir packages/expo exec prettier --check src/provider/singleton/createClerkInstance.ts src/provider/singleton/__tests__/createClerkInstance.test.ts ../../.changeset/clean-token-cache.md
  • git diff --check

Note: pnpm --filter @clerk/expo test -- createClerkInstance ran the new singleton test successfully, but the package-level pattern also collected unrelated hook tests that require built @clerk/react exports in this fresh worktree.

@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 1, 2026 3:03pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Jun 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 377a0a6

@mikepitre
mikepitre marked this pull request as ready for review June 1, 2026 15:14
@coderabbitai

coderabbitaiBot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR fixes a bug where class-based token cache methods lose their this context when stored by reference during Clerk singleton initialization. The implementation wraps tokenCache.getToken and tokenCache.saveToken in arrow functions that forward calls with explicit arguments, preserving the instance context. A regression test validates the fix by using a class-based token cache instance and verifying tokens are correctly cached and retrieved through the request hooks. The changeset documents the patch release.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): preserve token cache method context' accurately summarizes the main change: fixing a bug where token cache methods lose their 'this' context during integration.
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.
Description check✅ PassedThe description thoroughly explains the issue, provides context from a customer bug report, details the fix approach, shows both supported token cache styles, and includes testing instructions.

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


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

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

@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.

Nice, thanks for fixing!

@jacekradko
jacekradko merged commit e2b6a9f into mainJun 1, 2026
78 checks passed
@jacekradko
jacekradko deleted the mike/expo-token-cache-issue branch June 1, 2026 17:54
honerlaw added a commit to honerlaw/vital that referenced this pull request Jun 6, 2026
Two fixes for the TestFlight white-screen-at-launch incident:
- @clerk/expo 3.3.0 -> 3.3.1: upstream fix (clerk/javascript#8713)
preserves tokenCache method context when wiring the native Clerk
singleton into clerk-js request hooks. The bug class presents as
clerk load() failing silently -> isLoaded stuck false -> the splash
never hides (no exception thrown anywhere).
- expo-splash-screen plugin: the image/imageWidth lived only under the
android key, so prebuild generated an iOS storyboard with NO
customization at all - systemBackgroundColor (white) and no logo,
which is why the stuck splash reads as a blank white screen. Moving
image/imageWidth to the plugin root gives iOS the brand background
(#0D8348) + logo (verified in the generated SplashScreen.storyboard:
named color sRGB 0.051/0.514/0.282 + SplashScreenLogo 76px).
Also picks up prebuild's scripts rewrite (expo run:ios/run:android).
Verified: Release-configuration simulator build (Doppler dev env)
launches to the sign-in screen; lint/typecheck/test green.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

3 participants

@mikepitre@wobsoriano@jacekradko
, '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): preserve token cache method context by mikepitre · Pull Request #8713 · clerk/javascript · GitHub
Skip to content

fix(expo): preserve token cache method context - #8713

Merged
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue
Jun 1, 2026
Merged

fix(expo): preserve token cache method context#8713
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue

Conversation

@mikepitre

@mikepitremikepitre commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Preserve tokenCache method context when @clerk/expo wires the native Clerk singleton into clerk-js request hooks.
  • Add regression coverage for a class-instance token cache whose methods rely on this.
  • Add a patch changeset for @clerk/expo.
  • Plain Ticket

This was an issue with some updates internally in the clerk sdk, with how it uses the tokenCache. The previous recommended code for the tokenCache no longer worked as clerk destructured the getValue and setValue functions from the tokenCache internally. This broke the cache when implemented as a class instance and not a pure object as the functions are not bound to the class instance when they rely on instance properties from the tokenCache class.

It's worth noting that anyone using a standard setup with the recomended code from (older) clerk documentation prior to clerk exporting a tokenCache from ckerk itself will have a high chance to hit this issue, so I suggest changing the clerk react-native sdk to be backwards compatible with the older tokenCache class instance.

Customer Context

A customer upgrading to Expo 56 and the latest @clerk/expo reported that the app stayed stuck loading forever on <ClerkLoaded>, and calls such as useAuth().getToken() did not resolve on iOS Simulator 26.3.

They later narrowed it down to their older documented tokenCache implementation. Their cache was implemented as a class instance, and the cache methods depended on instance properties. Because @clerk/expo detached getToken and saveToken from the provided tokenCache, those methods lost their receiver and no longer had access to this.

This keeps both cache styles working.

Supported Token Cache Styles

Plain object cache:

consttokenCache={asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(key);},asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(key,token);},};<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Class instance cache:

classTokenCache{privateprefix='clerk:';asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(this.prefix+key);}asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(this.prefix+key,token);}}consttokenCache=newTokenCache();<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Testing

  • pnpm --dir packages/expo exec vitest run src/provider/singleton/__tests__/createClerkInstance.test.ts
  • pnpm --dir packages/expo exec prettier --check src/provider/singleton/createClerkInstance.ts src/provider/singleton/__tests__/createClerkInstance.test.ts ../../.changeset/clean-token-cache.md
  • git diff --check

Note: pnpm --filter @clerk/expo test -- createClerkInstance ran the new singleton test successfully, but the package-level pattern also collected unrelated hook tests that require built @clerk/react exports in this fresh worktree.

@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 1, 2026 3:03pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Jun 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 377a0a6

@mikepitre
mikepitre marked this pull request as ready for review June 1, 2026 15:14
@coderabbitai

coderabbitaiBot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR fixes a bug where class-based token cache methods lose their this context when stored by reference during Clerk singleton initialization. The implementation wraps tokenCache.getToken and tokenCache.saveToken in arrow functions that forward calls with explicit arguments, preserving the instance context. A regression test validates the fix by using a class-based token cache instance and verifying tokens are correctly cached and retrieved through the request hooks. The changeset documents the patch release.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): preserve token cache method context' accurately summarizes the main change: fixing a bug where token cache methods lose their 'this' context during integration.
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.
Description check✅ PassedThe description thoroughly explains the issue, provides context from a customer bug report, details the fix approach, shows both supported token cache styles, and includes testing instructions.

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


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

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

@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.

Nice, thanks for fixing!

@jacekradko
jacekradko merged commit e2b6a9f into mainJun 1, 2026
78 checks passed
@jacekradko
jacekradko deleted the mike/expo-token-cache-issue branch June 1, 2026 17:54
honerlaw added a commit to honerlaw/vital that referenced this pull request Jun 6, 2026
Two fixes for the TestFlight white-screen-at-launch incident:
- @clerk/expo 3.3.0 -> 3.3.1: upstream fix (clerk/javascript#8713)
preserves tokenCache method context when wiring the native Clerk
singleton into clerk-js request hooks. The bug class presents as
clerk load() failing silently -> isLoaded stuck false -> the splash
never hides (no exception thrown anywhere).
- expo-splash-screen plugin: the image/imageWidth lived only under the
android key, so prebuild generated an iOS storyboard with NO
customization at all - systemBackgroundColor (white) and no logo,
which is why the stuck splash reads as a blank white screen. Moving
image/imageWidth to the plugin root gives iOS the brand background
(#0D8348) + logo (verified in the generated SplashScreen.storyboard:
named color sRGB 0.051/0.514/0.282 + SplashScreenLogo 76px).
Also picks up prebuild's scripts rewrite (expo run:ios/run:android).
Verified: Release-configuration simulator build (Doppler dev env)
launches to the sign-in screen; lint/typecheck/test green.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

3 participants

@mikepitre@wobsoriano@jacekradko
, '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): preserve token cache method context by mikepitre · Pull Request #8713 · clerk/javascript · GitHub
Skip to content

fix(expo): preserve token cache method context - #8713

Merged
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue
Jun 1, 2026
Merged

fix(expo): preserve token cache method context#8713
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue

Conversation

@mikepitre

@mikepitremikepitre commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Preserve tokenCache method context when @clerk/expo wires the native Clerk singleton into clerk-js request hooks.
  • Add regression coverage for a class-instance token cache whose methods rely on this.
  • Add a patch changeset for @clerk/expo.
  • Plain Ticket

This was an issue with some updates internally in the clerk sdk, with how it uses the tokenCache. The previous recommended code for the tokenCache no longer worked as clerk destructured the getValue and setValue functions from the tokenCache internally. This broke the cache when implemented as a class instance and not a pure object as the functions are not bound to the class instance when they rely on instance properties from the tokenCache class.

It's worth noting that anyone using a standard setup with the recomended code from (older) clerk documentation prior to clerk exporting a tokenCache from ckerk itself will have a high chance to hit this issue, so I suggest changing the clerk react-native sdk to be backwards compatible with the older tokenCache class instance.

Customer Context

A customer upgrading to Expo 56 and the latest @clerk/expo reported that the app stayed stuck loading forever on <ClerkLoaded>, and calls such as useAuth().getToken() did not resolve on iOS Simulator 26.3.

They later narrowed it down to their older documented tokenCache implementation. Their cache was implemented as a class instance, and the cache methods depended on instance properties. Because @clerk/expo detached getToken and saveToken from the provided tokenCache, those methods lost their receiver and no longer had access to this.

This keeps both cache styles working.

Supported Token Cache Styles

Plain object cache:

consttokenCache={asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(key);},asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(key,token);},};<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Class instance cache:

classTokenCache{privateprefix='clerk:';asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(this.prefix+key);}asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(this.prefix+key,token);}}consttokenCache=newTokenCache();<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Testing

  • pnpm --dir packages/expo exec vitest run src/provider/singleton/__tests__/createClerkInstance.test.ts
  • pnpm --dir packages/expo exec prettier --check src/provider/singleton/createClerkInstance.ts src/provider/singleton/__tests__/createClerkInstance.test.ts ../../.changeset/clean-token-cache.md
  • git diff --check

Note: pnpm --filter @clerk/expo test -- createClerkInstance ran the new singleton test successfully, but the package-level pattern also collected unrelated hook tests that require built @clerk/react exports in this fresh worktree.

@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 1, 2026 3:03pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Jun 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 377a0a6

@mikepitre
mikepitre marked this pull request as ready for review June 1, 2026 15:14
@coderabbitai

coderabbitaiBot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR fixes a bug where class-based token cache methods lose their this context when stored by reference during Clerk singleton initialization. The implementation wraps tokenCache.getToken and tokenCache.saveToken in arrow functions that forward calls with explicit arguments, preserving the instance context. A regression test validates the fix by using a class-based token cache instance and verifying tokens are correctly cached and retrieved through the request hooks. The changeset documents the patch release.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): preserve token cache method context' accurately summarizes the main change: fixing a bug where token cache methods lose their 'this' context during integration.
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.
Description check✅ PassedThe description thoroughly explains the issue, provides context from a customer bug report, details the fix approach, shows both supported token cache styles, and includes testing instructions.

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


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

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

@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.

Nice, thanks for fixing!

@jacekradko
jacekradko merged commit e2b6a9f into mainJun 1, 2026
78 checks passed
@jacekradko
jacekradko deleted the mike/expo-token-cache-issue branch June 1, 2026 17:54
honerlaw added a commit to honerlaw/vital that referenced this pull request Jun 6, 2026
Two fixes for the TestFlight white-screen-at-launch incident:
- @clerk/expo 3.3.0 -> 3.3.1: upstream fix (clerk/javascript#8713)
preserves tokenCache method context when wiring the native Clerk
singleton into clerk-js request hooks. The bug class presents as
clerk load() failing silently -> isLoaded stuck false -> the splash
never hides (no exception thrown anywhere).
- expo-splash-screen plugin: the image/imageWidth lived only under the
android key, so prebuild generated an iOS storyboard with NO
customization at all - systemBackgroundColor (white) and no logo,
which is why the stuck splash reads as a blank white screen. Moving
image/imageWidth to the plugin root gives iOS the brand background
(#0D8348) + logo (verified in the generated SplashScreen.storyboard:
named color sRGB 0.051/0.514/0.282 + SplashScreenLogo 76px).
Also picks up prebuild's scripts rewrite (expo run:ios/run:android).
Verified: Release-configuration simulator build (Doppler dev env)
launches to the sign-in screen; lint/typecheck/test green.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

3 participants

@mikepitre@wobsoriano@jacekradko
, '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): preserve token cache method context by mikepitre · Pull Request #8713 · clerk/javascript · GitHub
Skip to content

fix(expo): preserve token cache method context - #8713

Merged
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue
Jun 1, 2026
Merged

fix(expo): preserve token cache method context#8713
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue

Conversation

@mikepitre

@mikepitremikepitre commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Preserve tokenCache method context when @clerk/expo wires the native Clerk singleton into clerk-js request hooks.
  • Add regression coverage for a class-instance token cache whose methods rely on this.
  • Add a patch changeset for @clerk/expo.
  • Plain Ticket

This was an issue with some updates internally in the clerk sdk, with how it uses the tokenCache. The previous recommended code for the tokenCache no longer worked as clerk destructured the getValue and setValue functions from the tokenCache internally. This broke the cache when implemented as a class instance and not a pure object as the functions are not bound to the class instance when they rely on instance properties from the tokenCache class.

It's worth noting that anyone using a standard setup with the recomended code from (older) clerk documentation prior to clerk exporting a tokenCache from ckerk itself will have a high chance to hit this issue, so I suggest changing the clerk react-native sdk to be backwards compatible with the older tokenCache class instance.

Customer Context

A customer upgrading to Expo 56 and the latest @clerk/expo reported that the app stayed stuck loading forever on <ClerkLoaded>, and calls such as useAuth().getToken() did not resolve on iOS Simulator 26.3.

They later narrowed it down to their older documented tokenCache implementation. Their cache was implemented as a class instance, and the cache methods depended on instance properties. Because @clerk/expo detached getToken and saveToken from the provided tokenCache, those methods lost their receiver and no longer had access to this.

This keeps both cache styles working.

Supported Token Cache Styles

Plain object cache:

consttokenCache={asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(key);},asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(key,token);},};<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Class instance cache:

classTokenCache{privateprefix='clerk:';asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(this.prefix+key);}asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(this.prefix+key,token);}}consttokenCache=newTokenCache();<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Testing

  • pnpm --dir packages/expo exec vitest run src/provider/singleton/__tests__/createClerkInstance.test.ts
  • pnpm --dir packages/expo exec prettier --check src/provider/singleton/createClerkInstance.ts src/provider/singleton/__tests__/createClerkInstance.test.ts ../../.changeset/clean-token-cache.md
  • git diff --check

Note: pnpm --filter @clerk/expo test -- createClerkInstance ran the new singleton test successfully, but the package-level pattern also collected unrelated hook tests that require built @clerk/react exports in this fresh worktree.

@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 1, 2026 3:03pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Jun 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 377a0a6

@mikepitre
mikepitre marked this pull request as ready for review June 1, 2026 15:14
@coderabbitai

coderabbitaiBot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR fixes a bug where class-based token cache methods lose their this context when stored by reference during Clerk singleton initialization. The implementation wraps tokenCache.getToken and tokenCache.saveToken in arrow functions that forward calls with explicit arguments, preserving the instance context. A regression test validates the fix by using a class-based token cache instance and verifying tokens are correctly cached and retrieved through the request hooks. The changeset documents the patch release.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): preserve token cache method context' accurately summarizes the main change: fixing a bug where token cache methods lose their 'this' context during integration.
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.
Description check✅ PassedThe description thoroughly explains the issue, provides context from a customer bug report, details the fix approach, shows both supported token cache styles, and includes testing instructions.

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


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

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

@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.

Nice, thanks for fixing!

@jacekradko
jacekradko merged commit e2b6a9f into mainJun 1, 2026
78 checks passed
@jacekradko
jacekradko deleted the mike/expo-token-cache-issue branch June 1, 2026 17:54
honerlaw added a commit to honerlaw/vital that referenced this pull request Jun 6, 2026
Two fixes for the TestFlight white-screen-at-launch incident:
- @clerk/expo 3.3.0 -> 3.3.1: upstream fix (clerk/javascript#8713)
preserves tokenCache method context when wiring the native Clerk
singleton into clerk-js request hooks. The bug class presents as
clerk load() failing silently -> isLoaded stuck false -> the splash
never hides (no exception thrown anywhere).
- expo-splash-screen plugin: the image/imageWidth lived only under the
android key, so prebuild generated an iOS storyboard with NO
customization at all - systemBackgroundColor (white) and no logo,
which is why the stuck splash reads as a blank white screen. Moving
image/imageWidth to the plugin root gives iOS the brand background
(#0D8348) + logo (verified in the generated SplashScreen.storyboard:
named color sRGB 0.051/0.514/0.282 + SplashScreenLogo 76px).
Also picks up prebuild's scripts rewrite (expo run:ios/run:android).
Verified: Release-configuration simulator build (Doppler dev env)
launches to the sign-in screen; lint/typecheck/test green.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

3 participants

@mikepitre@wobsoriano@jacekradko
, '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): preserve token cache method context by mikepitre · Pull Request #8713 · clerk/javascript · GitHub
Skip to content

fix(expo): preserve token cache method context - #8713

Merged
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue
Jun 1, 2026
Merged

fix(expo): preserve token cache method context#8713
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue

Conversation

@mikepitre

@mikepitremikepitre commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Preserve tokenCache method context when @clerk/expo wires the native Clerk singleton into clerk-js request hooks.
  • Add regression coverage for a class-instance token cache whose methods rely on this.
  • Add a patch changeset for @clerk/expo.
  • Plain Ticket

This was an issue with some updates internally in the clerk sdk, with how it uses the tokenCache. The previous recommended code for the tokenCache no longer worked as clerk destructured the getValue and setValue functions from the tokenCache internally. This broke the cache when implemented as a class instance and not a pure object as the functions are not bound to the class instance when they rely on instance properties from the tokenCache class.

It's worth noting that anyone using a standard setup with the recomended code from (older) clerk documentation prior to clerk exporting a tokenCache from ckerk itself will have a high chance to hit this issue, so I suggest changing the clerk react-native sdk to be backwards compatible with the older tokenCache class instance.

Customer Context

A customer upgrading to Expo 56 and the latest @clerk/expo reported that the app stayed stuck loading forever on <ClerkLoaded>, and calls such as useAuth().getToken() did not resolve on iOS Simulator 26.3.

They later narrowed it down to their older documented tokenCache implementation. Their cache was implemented as a class instance, and the cache methods depended on instance properties. Because @clerk/expo detached getToken and saveToken from the provided tokenCache, those methods lost their receiver and no longer had access to this.

This keeps both cache styles working.

Supported Token Cache Styles

Plain object cache:

consttokenCache={asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(key);},asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(key,token);},};<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Class instance cache:

classTokenCache{privateprefix='clerk:';asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(this.prefix+key);}asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(this.prefix+key,token);}}consttokenCache=newTokenCache();<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Testing

  • pnpm --dir packages/expo exec vitest run src/provider/singleton/__tests__/createClerkInstance.test.ts
  • pnpm --dir packages/expo exec prettier --check src/provider/singleton/createClerkInstance.ts src/provider/singleton/__tests__/createClerkInstance.test.ts ../../.changeset/clean-token-cache.md
  • git diff --check

Note: pnpm --filter @clerk/expo test -- createClerkInstance ran the new singleton test successfully, but the package-level pattern also collected unrelated hook tests that require built @clerk/react exports in this fresh worktree.

@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 1, 2026 3:03pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Jun 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 377a0a6

@mikepitre
mikepitre marked this pull request as ready for review June 1, 2026 15:14
@coderabbitai

coderabbitaiBot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR fixes a bug where class-based token cache methods lose their this context when stored by reference during Clerk singleton initialization. The implementation wraps tokenCache.getToken and tokenCache.saveToken in arrow functions that forward calls with explicit arguments, preserving the instance context. A regression test validates the fix by using a class-based token cache instance and verifying tokens are correctly cached and retrieved through the request hooks. The changeset documents the patch release.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): preserve token cache method context' accurately summarizes the main change: fixing a bug where token cache methods lose their 'this' context during integration.
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.
Description check✅ PassedThe description thoroughly explains the issue, provides context from a customer bug report, details the fix approach, shows both supported token cache styles, and includes testing instructions.

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


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

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

@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.

Nice, thanks for fixing!

@jacekradko
jacekradko merged commit e2b6a9f into mainJun 1, 2026
78 checks passed
@jacekradko
jacekradko deleted the mike/expo-token-cache-issue branch June 1, 2026 17:54
honerlaw added a commit to honerlaw/vital that referenced this pull request Jun 6, 2026
Two fixes for the TestFlight white-screen-at-launch incident:
- @clerk/expo 3.3.0 -> 3.3.1: upstream fix (clerk/javascript#8713)
preserves tokenCache method context when wiring the native Clerk
singleton into clerk-js request hooks. The bug class presents as
clerk load() failing silently -> isLoaded stuck false -> the splash
never hides (no exception thrown anywhere).
- expo-splash-screen plugin: the image/imageWidth lived only under the
android key, so prebuild generated an iOS storyboard with NO
customization at all - systemBackgroundColor (white) and no logo,
which is why the stuck splash reads as a blank white screen. Moving
image/imageWidth to the plugin root gives iOS the brand background
(#0D8348) + logo (verified in the generated SplashScreen.storyboard:
named color sRGB 0.051/0.514/0.282 + SplashScreenLogo 76px).
Also picks up prebuild's scripts rewrite (expo run:ios/run:android).
Verified: Release-configuration simulator build (Doppler dev env)
launches to the sign-in screen; lint/typecheck/test green.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

3 participants

@mikepitre@wobsoriano@jacekradko
, '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): preserve token cache method context by mikepitre · Pull Request #8713 · clerk/javascript · GitHub
Skip to content

fix(expo): preserve token cache method context - #8713

Merged
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue
Jun 1, 2026
Merged

fix(expo): preserve token cache method context#8713
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue

Conversation

@mikepitre

@mikepitremikepitre commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Preserve tokenCache method context when @clerk/expo wires the native Clerk singleton into clerk-js request hooks.
  • Add regression coverage for a class-instance token cache whose methods rely on this.
  • Add a patch changeset for @clerk/expo.
  • Plain Ticket

This was an issue with some updates internally in the clerk sdk, with how it uses the tokenCache. The previous recommended code for the tokenCache no longer worked as clerk destructured the getValue and setValue functions from the tokenCache internally. This broke the cache when implemented as a class instance and not a pure object as the functions are not bound to the class instance when they rely on instance properties from the tokenCache class.

It's worth noting that anyone using a standard setup with the recomended code from (older) clerk documentation prior to clerk exporting a tokenCache from ckerk itself will have a high chance to hit this issue, so I suggest changing the clerk react-native sdk to be backwards compatible with the older tokenCache class instance.

Customer Context

A customer upgrading to Expo 56 and the latest @clerk/expo reported that the app stayed stuck loading forever on <ClerkLoaded>, and calls such as useAuth().getToken() did not resolve on iOS Simulator 26.3.

They later narrowed it down to their older documented tokenCache implementation. Their cache was implemented as a class instance, and the cache methods depended on instance properties. Because @clerk/expo detached getToken and saveToken from the provided tokenCache, those methods lost their receiver and no longer had access to this.

This keeps both cache styles working.

Supported Token Cache Styles

Plain object cache:

consttokenCache={asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(key);},asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(key,token);},};<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Class instance cache:

classTokenCache{privateprefix='clerk:';asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(this.prefix+key);}asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(this.prefix+key,token);}}consttokenCache=newTokenCache();<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Testing

  • pnpm --dir packages/expo exec vitest run src/provider/singleton/__tests__/createClerkInstance.test.ts
  • pnpm --dir packages/expo exec prettier --check src/provider/singleton/createClerkInstance.ts src/provider/singleton/__tests__/createClerkInstance.test.ts ../../.changeset/clean-token-cache.md
  • git diff --check

Note: pnpm --filter @clerk/expo test -- createClerkInstance ran the new singleton test successfully, but the package-level pattern also collected unrelated hook tests that require built @clerk/react exports in this fresh worktree.

@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 1, 2026 3:03pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Jun 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 377a0a6

@mikepitre
mikepitre marked this pull request as ready for review June 1, 2026 15:14
@coderabbitai

coderabbitaiBot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR fixes a bug where class-based token cache methods lose their this context when stored by reference during Clerk singleton initialization. The implementation wraps tokenCache.getToken and tokenCache.saveToken in arrow functions that forward calls with explicit arguments, preserving the instance context. A regression test validates the fix by using a class-based token cache instance and verifying tokens are correctly cached and retrieved through the request hooks. The changeset documents the patch release.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): preserve token cache method context' accurately summarizes the main change: fixing a bug where token cache methods lose their 'this' context during integration.
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.
Description check✅ PassedThe description thoroughly explains the issue, provides context from a customer bug report, details the fix approach, shows both supported token cache styles, and includes testing instructions.

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


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

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

@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.

Nice, thanks for fixing!

@jacekradko
jacekradko merged commit e2b6a9f into mainJun 1, 2026
78 checks passed
@jacekradko
jacekradko deleted the mike/expo-token-cache-issue branch June 1, 2026 17:54
honerlaw added a commit to honerlaw/vital that referenced this pull request Jun 6, 2026
Two fixes for the TestFlight white-screen-at-launch incident:
- @clerk/expo 3.3.0 -> 3.3.1: upstream fix (clerk/javascript#8713)
preserves tokenCache method context when wiring the native Clerk
singleton into clerk-js request hooks. The bug class presents as
clerk load() failing silently -> isLoaded stuck false -> the splash
never hides (no exception thrown anywhere).
- expo-splash-screen plugin: the image/imageWidth lived only under the
android key, so prebuild generated an iOS storyboard with NO
customization at all - systemBackgroundColor (white) and no logo,
which is why the stuck splash reads as a blank white screen. Moving
image/imageWidth to the plugin root gives iOS the brand background
(#0D8348) + logo (verified in the generated SplashScreen.storyboard:
named color sRGB 0.051/0.514/0.282 + SplashScreenLogo 76px).
Also picks up prebuild's scripts rewrite (expo run:ios/run:android).
Verified: Release-configuration simulator build (Doppler dev env)
launches to the sign-in screen; lint/typecheck/test green.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

3 participants

@mikepitre@wobsoriano@jacekradko
, '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): preserve token cache method context by mikepitre · Pull Request #8713 · clerk/javascript · GitHub
Skip to content

fix(expo): preserve token cache method context - #8713

Merged
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue
Jun 1, 2026
Merged

fix(expo): preserve token cache method context#8713
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue

Conversation

@mikepitre

@mikepitremikepitre commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Preserve tokenCache method context when @clerk/expo wires the native Clerk singleton into clerk-js request hooks.
  • Add regression coverage for a class-instance token cache whose methods rely on this.
  • Add a patch changeset for @clerk/expo.
  • Plain Ticket

This was an issue with some updates internally in the clerk sdk, with how it uses the tokenCache. The previous recommended code for the tokenCache no longer worked as clerk destructured the getValue and setValue functions from the tokenCache internally. This broke the cache when implemented as a class instance and not a pure object as the functions are not bound to the class instance when they rely on instance properties from the tokenCache class.

It's worth noting that anyone using a standard setup with the recomended code from (older) clerk documentation prior to clerk exporting a tokenCache from ckerk itself will have a high chance to hit this issue, so I suggest changing the clerk react-native sdk to be backwards compatible with the older tokenCache class instance.

Customer Context

A customer upgrading to Expo 56 and the latest @clerk/expo reported that the app stayed stuck loading forever on <ClerkLoaded>, and calls such as useAuth().getToken() did not resolve on iOS Simulator 26.3.

They later narrowed it down to their older documented tokenCache implementation. Their cache was implemented as a class instance, and the cache methods depended on instance properties. Because @clerk/expo detached getToken and saveToken from the provided tokenCache, those methods lost their receiver and no longer had access to this.

This keeps both cache styles working.

Supported Token Cache Styles

Plain object cache:

consttokenCache={asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(key);},asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(key,token);},};<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Class instance cache:

classTokenCache{privateprefix='clerk:';asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(this.prefix+key);}asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(this.prefix+key,token);}}consttokenCache=newTokenCache();<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Testing

  • pnpm --dir packages/expo exec vitest run src/provider/singleton/__tests__/createClerkInstance.test.ts
  • pnpm --dir packages/expo exec prettier --check src/provider/singleton/createClerkInstance.ts src/provider/singleton/__tests__/createClerkInstance.test.ts ../../.changeset/clean-token-cache.md
  • git diff --check

Note: pnpm --filter @clerk/expo test -- createClerkInstance ran the new singleton test successfully, but the package-level pattern also collected unrelated hook tests that require built @clerk/react exports in this fresh worktree.

@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 1, 2026 3:03pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Jun 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 377a0a6

@mikepitre
mikepitre marked this pull request as ready for review June 1, 2026 15:14
@coderabbitai

coderabbitaiBot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR fixes a bug where class-based token cache methods lose their this context when stored by reference during Clerk singleton initialization. The implementation wraps tokenCache.getToken and tokenCache.saveToken in arrow functions that forward calls with explicit arguments, preserving the instance context. A regression test validates the fix by using a class-based token cache instance and verifying tokens are correctly cached and retrieved through the request hooks. The changeset documents the patch release.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): preserve token cache method context' accurately summarizes the main change: fixing a bug where token cache methods lose their 'this' context during integration.
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.
Description check✅ PassedThe description thoroughly explains the issue, provides context from a customer bug report, details the fix approach, shows both supported token cache styles, and includes testing instructions.

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


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

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

@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.

Nice, thanks for fixing!

@jacekradko
jacekradko merged commit e2b6a9f into mainJun 1, 2026
78 checks passed
@jacekradko
jacekradko deleted the mike/expo-token-cache-issue branch June 1, 2026 17:54
honerlaw added a commit to honerlaw/vital that referenced this pull request Jun 6, 2026
Two fixes for the TestFlight white-screen-at-launch incident:
- @clerk/expo 3.3.0 -> 3.3.1: upstream fix (clerk/javascript#8713)
preserves tokenCache method context when wiring the native Clerk
singleton into clerk-js request hooks. The bug class presents as
clerk load() failing silently -> isLoaded stuck false -> the splash
never hides (no exception thrown anywhere).
- expo-splash-screen plugin: the image/imageWidth lived only under the
android key, so prebuild generated an iOS storyboard with NO
customization at all - systemBackgroundColor (white) and no logo,
which is why the stuck splash reads as a blank white screen. Moving
image/imageWidth to the plugin root gives iOS the brand background
(#0D8348) + logo (verified in the generated SplashScreen.storyboard:
named color sRGB 0.051/0.514/0.282 + SplashScreenLogo 76px).
Also picks up prebuild's scripts rewrite (expo run:ios/run:android).
Verified: Release-configuration simulator build (Doppler dev env)
launches to the sign-in screen; lint/typecheck/test green.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

3 participants

@mikepitre@wobsoriano@jacekradko
, '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): preserve token cache method context by mikepitre · Pull Request #8713 · clerk/javascript · GitHub
Skip to content

fix(expo): preserve token cache method context - #8713

Merged
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue
Jun 1, 2026
Merged

fix(expo): preserve token cache method context#8713
jacekradko merged 1 commit into
mainfrom
mike/expo-token-cache-issue

Conversation

@mikepitre

@mikepitremikepitre commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Preserve tokenCache method context when @clerk/expo wires the native Clerk singleton into clerk-js request hooks.
  • Add regression coverage for a class-instance token cache whose methods rely on this.
  • Add a patch changeset for @clerk/expo.
  • Plain Ticket

This was an issue with some updates internally in the clerk sdk, with how it uses the tokenCache. The previous recommended code for the tokenCache no longer worked as clerk destructured the getValue and setValue functions from the tokenCache internally. This broke the cache when implemented as a class instance and not a pure object as the functions are not bound to the class instance when they rely on instance properties from the tokenCache class.

It's worth noting that anyone using a standard setup with the recomended code from (older) clerk documentation prior to clerk exporting a tokenCache from ckerk itself will have a high chance to hit this issue, so I suggest changing the clerk react-native sdk to be backwards compatible with the older tokenCache class instance.

Customer Context

A customer upgrading to Expo 56 and the latest @clerk/expo reported that the app stayed stuck loading forever on <ClerkLoaded>, and calls such as useAuth().getToken() did not resolve on iOS Simulator 26.3.

They later narrowed it down to their older documented tokenCache implementation. Their cache was implemented as a class instance, and the cache methods depended on instance properties. Because @clerk/expo detached getToken and saveToken from the provided tokenCache, those methods lost their receiver and no longer had access to this.

This keeps both cache styles working.

Supported Token Cache Styles

Plain object cache:

consttokenCache={asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(key);},asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(key,token);},};<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Class instance cache:

classTokenCache{privateprefix='clerk:';asyncgetToken(key: string){returnawaitSecureStore.getItemAsync(this.prefix+key);}asyncsaveToken(key: string,token: string){awaitSecureStore.setItemAsync(this.prefix+key,token);}}consttokenCache=newTokenCache();<ClerkProviderpublishableKey={publishableKey}tokenCache={tokenCache}>{children}</ClerkProvider>;

Testing

  • pnpm --dir packages/expo exec vitest run src/provider/singleton/__tests__/createClerkInstance.test.ts
  • pnpm --dir packages/expo exec prettier --check src/provider/singleton/createClerkInstance.ts src/provider/singleton/__tests__/createClerkInstance.test.ts ../../.changeset/clean-token-cache.md
  • git diff --check

Note: pnpm --filter @clerk/expo test -- createClerkInstance ran the new singleton test successfully, but the package-level pattern also collected unrelated hook tests that require built @clerk/react exports in this fresh worktree.

@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 1, 2026 3:03pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Jun 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 377a0a6

@mikepitre
mikepitre marked this pull request as ready for review June 1, 2026 15:14
@coderabbitai

coderabbitaiBot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR fixes a bug where class-based token cache methods lose their this context when stored by reference during Clerk singleton initialization. The implementation wraps tokenCache.getToken and tokenCache.saveToken in arrow functions that forward calls with explicit arguments, preserving the instance context. A regression test validates the fix by using a class-based token cache instance and verifying tokens are correctly cached and retrieved through the request hooks. The changeset documents the patch release.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title 'fix(expo): preserve token cache method context' accurately summarizes the main change: fixing a bug where token cache methods lose their 'this' context during integration.
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.
Description check✅ PassedThe description thoroughly explains the issue, provides context from a customer bug report, details the fix approach, shows both supported token cache styles, and includes testing instructions.

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


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

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

@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.

Nice, thanks for fixing!

@jacekradko
jacekradko merged commit e2b6a9f into mainJun 1, 2026
78 checks passed
@jacekradko
jacekradko deleted the mike/expo-token-cache-issue branch June 1, 2026 17:54
honerlaw added a commit to honerlaw/vital that referenced this pull request Jun 6, 2026
Two fixes for the TestFlight white-screen-at-launch incident:
- @clerk/expo 3.3.0 -> 3.3.1: upstream fix (clerk/javascript#8713)
preserves tokenCache method context when wiring the native Clerk
singleton into clerk-js request hooks. The bug class presents as
clerk load() failing silently -> isLoaded stuck false -> the splash
never hides (no exception thrown anywhere).
- expo-splash-screen plugin: the image/imageWidth lived only under the
android key, so prebuild generated an iOS storyboard with NO
customization at all - systemBackgroundColor (white) and no logo,
which is why the stuck splash reads as a blank white screen. Moving
image/imageWidth to the plugin root gives iOS the brand background
(#0D8348) + logo (verified in the generated SplashScreen.storyboard:
named color sRGB 0.051/0.514/0.282 + SplashScreenLogo 76px).
Also picks up prebuild's scripts rewrite (expo run:ios/run:android).
Verified: Release-configuration simulator build (Doppler dev env)
launches to the sign-in screen; lint/typecheck/test green.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

3 participants

@mikepitre@wobsoriano@jacekradko