fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485) - #8485

Merged
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix
May 6, 2026
Merged

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485)#8485
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Summary

Fixes MOBILE-485.

Customer-reported bug (T-57530, M2X Group, Business tier): on @clerk/expo@3.2.x, the first tap on "Sign in with Google" in <AuthView /> after a cold launch silently fails. Logcat shows:

E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context.

The workaround was to background and foreground the app once before signing in.

Root cause

clerk-android tracks the current foreground Activity via ActivityLifecycleCallbacks registered inside Clerk.initialize(). The callbacks only fire on subsequent onActivityCreated/Started/Resumed events — they don't observe the host Activity's current state at the moment initialize() runs.

In React Native, MainActivity has already passed onResume() by the time <ClerkProvider /> mounts and configure() runs, so when we call Clerk.initialize(), the just-registered callbacks miss the initial Activity. Clerk.currentActivity stays null until the next OS-driven resume cycle — which is why backgrounding and re-foregrounding the app "fixes" it.

Fix

The proper fix lives in clerk-android: it now exposes a public Clerk.attachActivity(activity) API in 1.0.16 for hosts that pass an Application-typed Context to initialize() but have a current Activity available.

This PR adopts that API at two reliable points:

  1. ClerkExpoModule.configure() — right after Clerk.initialize(), calls Clerk.attachActivity(getCurrentActivity()). May be a no-op on cold start if React's host-resume sync hasn't fired yet, but covers the warm-init case.

  2. ClerkAuthNativeView constructor — calls Clerk.attachActivity() with the Activity returned by findActivity(context). This is the empirically-reliable backstop: by the time the AuthView is being constructed, the host Activity is unambiguously available.

Bumps clerk-android-{api,ui} to 1.0.16. Also adds an explicit clerk-android-api dep — without it, telemetry's transitive pin on the prior release wins the version conflict and consumers end up on the older artifact (which doesn't have attachActivity).

Test plan

  • Reproduced the bug first-hand on @clerk/expo@3.2.7 against a Pixel 9 Pro emulator (Android 16). Cold-launch via pm clear + deep-link, tap Google as first action — E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context. in logcat, silent failure on JS, stuck on AuthView.
  • Built @clerk/expo against this branch (consuming clerk-android 1.0.16 via local Maven), reran the same reproduction:
    • I/CredentialManager: starting executeGetCredential[GetGoogleIdOperation] Operation succeeded → Google account picker opens, sign-in completes, lands on the Welcome screen.
    • No MissingActivity in logcat.
  • Re-ran the exact reproduction with the fix in place after a clean pm clear and force-stop — could not reproduce the error.

Gating

This PR depends on clerk/clerk-android#614 shipping as 1.0.16. The build will fail at the clerk_expo:compileDebugKotlin step until that release is published to Maven Central.

…ty (MOBILE-485)
clerk-android tracks the current foreground Activity via
ActivityLifecycleCallbacks registered inside Clerk.initialize(). In a
React Native app, MainActivity has already passed onResume() by the time
<ClerkProvider> mounts and configure() runs, so the callbacks miss the
initial Activity. Without seeding, the first Credential Manager call
(Google sign-in, passkeys) fails with MissingActivity until the user
backgrounds and foregrounds the app.
Adopt the new public Clerk.attachActivity() API from clerk-android 1.0.16
to seed the Activity reference at two reliable points: in
ClerkExpoModule.configure() right after Clerk.initialize() (using
React's currentActivity), and in ClerkAuthNativeView's findActivity()
result. The second hook is the empirically-reliable backstop — at cold
start, getCurrentActivity() may return null before React's host-resume
sync, but ClerkAuthNativeView is constructed when AuthView mounts, by
which point the Activity is unambiguously available.
Bumps clerk-android-{api,ui} to 1.0.16. Adds an explicit clerk-android-api
dep to override telemetry's transitive pin on the previous release —
without this the version constraint loses the conflict and consumers end
up on the older artifact (which lacks attachActivity).
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5aecdf2

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

@vercel

vercelBot commented May 5, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 5, 2026 11:04pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented May 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 5aecdf2

@coderabbitai

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request addresses MissingActivity errors during cold-start sign-in flows by ensuring the Android Activity is registered with the Clerk SDK before credential operations. The changes include: (1) updating Android dependency versions from 1.0.13 to 1.0.16 and adding the clerk-android-api dependency; (2) invoking Clerk.attachActivity() at SDK initialization in ClerkExpoModule and during view initialization in ClerkAuthExpoView; and (3) documenting the patch in a changeset. No public API signatures are modified, and the fix is transparent on rebuild.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically identifies the main fix: seeding the Clerk activity reference on cold-start to resolve the MissingActivity bug, with the issue ticket reference for traceability.
Description check✅ PassedThe description thoroughly explains the customer-reported bug, root cause analysis, the implemented fix across multiple files, dependency bumps, and comprehensive test results—all directly relevant to the changeset.
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.


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt (1)

72-162: ⚡ Quick win

No automated tests cover the cold-start fix

No tests are added or modified in this PR. Per project guidelines, test coverage is expected for changed paths. At minimum, a unit test mocking Clerk.isInitialized, getCurrentActivity(), and verifying Clerk.attachActivity() is called (or gracefully skipped when null) in both configure() and the ClerkAuthNativeView constructor would prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`
around lines 72 - 162, The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`:
- Around line 72-162: The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: 97a113e5-4d3a-424b-86f0-729ba5c023a0

📥 Commits

Reviewing files that changed from the base of the PR and between 30ec271 and 5aecdf2.

📒 Files selected for processing (4)
  • .changeset/expo-mobile-485-cold-start-activity.md
  • packages/expo/android/build.gradle
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt

@wobsoriano
wobsoriano merged commit 3b74d9a into mainMay 6, 2026
73 of 74 checks passed
@wobsoriano
wobsoriano deleted the chris/mobile-485-cold-start-fix branch May 6, 2026 16:54
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.

2 participants

@chriscanin@wobsoriano
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n 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;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485) - #8485

Merged
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix
May 6, 2026
Merged

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485)#8485
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Summary

Fixes MOBILE-485.

Customer-reported bug (T-57530, M2X Group, Business tier): on @clerk/expo@3.2.x, the first tap on "Sign in with Google" in <AuthView /> after a cold launch silently fails. Logcat shows:

E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context.

The workaround was to background and foreground the app once before signing in.

Root cause

clerk-android tracks the current foreground Activity via ActivityLifecycleCallbacks registered inside Clerk.initialize(). The callbacks only fire on subsequent onActivityCreated/Started/Resumed events — they don't observe the host Activity's current state at the moment initialize() runs.

In React Native, MainActivity has already passed onResume() by the time <ClerkProvider /> mounts and configure() runs, so when we call Clerk.initialize(), the just-registered callbacks miss the initial Activity. Clerk.currentActivity stays null until the next OS-driven resume cycle — which is why backgrounding and re-foregrounding the app "fixes" it.

Fix

The proper fix lives in clerk-android: it now exposes a public Clerk.attachActivity(activity) API in 1.0.16 for hosts that pass an Application-typed Context to initialize() but have a current Activity available.

This PR adopts that API at two reliable points:

  1. ClerkExpoModule.configure() — right after Clerk.initialize(), calls Clerk.attachActivity(getCurrentActivity()). May be a no-op on cold start if React's host-resume sync hasn't fired yet, but covers the warm-init case.

  2. ClerkAuthNativeView constructor — calls Clerk.attachActivity() with the Activity returned by findActivity(context). This is the empirically-reliable backstop: by the time the AuthView is being constructed, the host Activity is unambiguously available.

Bumps clerk-android-{api,ui} to 1.0.16. Also adds an explicit clerk-android-api dep — without it, telemetry's transitive pin on the prior release wins the version conflict and consumers end up on the older artifact (which doesn't have attachActivity).

Test plan

  • Reproduced the bug first-hand on @clerk/expo@3.2.7 against a Pixel 9 Pro emulator (Android 16). Cold-launch via pm clear + deep-link, tap Google as first action — E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context. in logcat, silent failure on JS, stuck on AuthView.
  • Built @clerk/expo against this branch (consuming clerk-android 1.0.16 via local Maven), reran the same reproduction:
    • I/CredentialManager: starting executeGetCredential[GetGoogleIdOperation] Operation succeeded → Google account picker opens, sign-in completes, lands on the Welcome screen.
    • No MissingActivity in logcat.
  • Re-ran the exact reproduction with the fix in place after a clean pm clear and force-stop — could not reproduce the error.

Gating

This PR depends on clerk/clerk-android#614 shipping as 1.0.16. The build will fail at the clerk_expo:compileDebugKotlin step until that release is published to Maven Central.

…ty (MOBILE-485)
clerk-android tracks the current foreground Activity via
ActivityLifecycleCallbacks registered inside Clerk.initialize(). In a
React Native app, MainActivity has already passed onResume() by the time
<ClerkProvider> mounts and configure() runs, so the callbacks miss the
initial Activity. Without seeding, the first Credential Manager call
(Google sign-in, passkeys) fails with MissingActivity until the user
backgrounds and foregrounds the app.
Adopt the new public Clerk.attachActivity() API from clerk-android 1.0.16
to seed the Activity reference at two reliable points: in
ClerkExpoModule.configure() right after Clerk.initialize() (using
React's currentActivity), and in ClerkAuthNativeView's findActivity()
result. The second hook is the empirically-reliable backstop — at cold
start, getCurrentActivity() may return null before React's host-resume
sync, but ClerkAuthNativeView is constructed when AuthView mounts, by
which point the Activity is unambiguously available.
Bumps clerk-android-{api,ui} to 1.0.16. Adds an explicit clerk-android-api
dep to override telemetry's transitive pin on the previous release —
without this the version constraint loses the conflict and consumers end
up on the older artifact (which lacks attachActivity).
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5aecdf2

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

@vercel

vercelBot commented May 5, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 5, 2026 11:04pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented May 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 5aecdf2

@coderabbitai

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request addresses MissingActivity errors during cold-start sign-in flows by ensuring the Android Activity is registered with the Clerk SDK before credential operations. The changes include: (1) updating Android dependency versions from 1.0.13 to 1.0.16 and adding the clerk-android-api dependency; (2) invoking Clerk.attachActivity() at SDK initialization in ClerkExpoModule and during view initialization in ClerkAuthExpoView; and (3) documenting the patch in a changeset. No public API signatures are modified, and the fix is transparent on rebuild.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically identifies the main fix: seeding the Clerk activity reference on cold-start to resolve the MissingActivity bug, with the issue ticket reference for traceability.
Description check✅ PassedThe description thoroughly explains the customer-reported bug, root cause analysis, the implemented fix across multiple files, dependency bumps, and comprehensive test results—all directly relevant to the changeset.
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.


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt (1)

72-162: ⚡ Quick win

No automated tests cover the cold-start fix

No tests are added or modified in this PR. Per project guidelines, test coverage is expected for changed paths. At minimum, a unit test mocking Clerk.isInitialized, getCurrentActivity(), and verifying Clerk.attachActivity() is called (or gracefully skipped when null) in both configure() and the ClerkAuthNativeView constructor would prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`
around lines 72 - 162, The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`:
- Around line 72-162: The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: 97a113e5-4d3a-424b-86f0-729ba5c023a0

📥 Commits

Reviewing files that changed from the base of the PR and between 30ec271 and 5aecdf2.

📒 Files selected for processing (4)
  • .changeset/expo-mobile-485-cold-start-activity.md
  • packages/expo/android/build.gradle
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt

@wobsoriano
wobsoriano merged commit 3b74d9a into mainMay 6, 2026
73 of 74 checks passed
@wobsoriano
wobsoriano deleted the chris/mobile-485-cold-start-fix branch May 6, 2026 16:54
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.

2 participants

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

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485) - #8485

Merged
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix
May 6, 2026
Merged

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485)#8485
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Summary

Fixes MOBILE-485.

Customer-reported bug (T-57530, M2X Group, Business tier): on @clerk/expo@3.2.x, the first tap on "Sign in with Google" in <AuthView /> after a cold launch silently fails. Logcat shows:

E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context.

The workaround was to background and foreground the app once before signing in.

Root cause

clerk-android tracks the current foreground Activity via ActivityLifecycleCallbacks registered inside Clerk.initialize(). The callbacks only fire on subsequent onActivityCreated/Started/Resumed events — they don't observe the host Activity's current state at the moment initialize() runs.

In React Native, MainActivity has already passed onResume() by the time <ClerkProvider /> mounts and configure() runs, so when we call Clerk.initialize(), the just-registered callbacks miss the initial Activity. Clerk.currentActivity stays null until the next OS-driven resume cycle — which is why backgrounding and re-foregrounding the app "fixes" it.

Fix

The proper fix lives in clerk-android: it now exposes a public Clerk.attachActivity(activity) API in 1.0.16 for hosts that pass an Application-typed Context to initialize() but have a current Activity available.

This PR adopts that API at two reliable points:

  1. ClerkExpoModule.configure() — right after Clerk.initialize(), calls Clerk.attachActivity(getCurrentActivity()). May be a no-op on cold start if React's host-resume sync hasn't fired yet, but covers the warm-init case.

  2. ClerkAuthNativeView constructor — calls Clerk.attachActivity() with the Activity returned by findActivity(context). This is the empirically-reliable backstop: by the time the AuthView is being constructed, the host Activity is unambiguously available.

Bumps clerk-android-{api,ui} to 1.0.16. Also adds an explicit clerk-android-api dep — without it, telemetry's transitive pin on the prior release wins the version conflict and consumers end up on the older artifact (which doesn't have attachActivity).

Test plan

  • Reproduced the bug first-hand on @clerk/expo@3.2.7 against a Pixel 9 Pro emulator (Android 16). Cold-launch via pm clear + deep-link, tap Google as first action — E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context. in logcat, silent failure on JS, stuck on AuthView.
  • Built @clerk/expo against this branch (consuming clerk-android 1.0.16 via local Maven), reran the same reproduction:
    • I/CredentialManager: starting executeGetCredential[GetGoogleIdOperation] Operation succeeded → Google account picker opens, sign-in completes, lands on the Welcome screen.
    • No MissingActivity in logcat.
  • Re-ran the exact reproduction with the fix in place after a clean pm clear and force-stop — could not reproduce the error.

Gating

This PR depends on clerk/clerk-android#614 shipping as 1.0.16. The build will fail at the clerk_expo:compileDebugKotlin step until that release is published to Maven Central.

…ty (MOBILE-485)
clerk-android tracks the current foreground Activity via
ActivityLifecycleCallbacks registered inside Clerk.initialize(). In a
React Native app, MainActivity has already passed onResume() by the time
<ClerkProvider> mounts and configure() runs, so the callbacks miss the
initial Activity. Without seeding, the first Credential Manager call
(Google sign-in, passkeys) fails with MissingActivity until the user
backgrounds and foregrounds the app.
Adopt the new public Clerk.attachActivity() API from clerk-android 1.0.16
to seed the Activity reference at two reliable points: in
ClerkExpoModule.configure() right after Clerk.initialize() (using
React's currentActivity), and in ClerkAuthNativeView's findActivity()
result. The second hook is the empirically-reliable backstop — at cold
start, getCurrentActivity() may return null before React's host-resume
sync, but ClerkAuthNativeView is constructed when AuthView mounts, by
which point the Activity is unambiguously available.
Bumps clerk-android-{api,ui} to 1.0.16. Adds an explicit clerk-android-api
dep to override telemetry's transitive pin on the previous release —
without this the version constraint loses the conflict and consumers end
up on the older artifact (which lacks attachActivity).
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5aecdf2

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

@vercel

vercelBot commented May 5, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 5, 2026 11:04pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented May 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 5aecdf2

@coderabbitai

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request addresses MissingActivity errors during cold-start sign-in flows by ensuring the Android Activity is registered with the Clerk SDK before credential operations. The changes include: (1) updating Android dependency versions from 1.0.13 to 1.0.16 and adding the clerk-android-api dependency; (2) invoking Clerk.attachActivity() at SDK initialization in ClerkExpoModule and during view initialization in ClerkAuthExpoView; and (3) documenting the patch in a changeset. No public API signatures are modified, and the fix is transparent on rebuild.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically identifies the main fix: seeding the Clerk activity reference on cold-start to resolve the MissingActivity bug, with the issue ticket reference for traceability.
Description check✅ PassedThe description thoroughly explains the customer-reported bug, root cause analysis, the implemented fix across multiple files, dependency bumps, and comprehensive test results—all directly relevant to the changeset.
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.


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt (1)

72-162: ⚡ Quick win

No automated tests cover the cold-start fix

No tests are added or modified in this PR. Per project guidelines, test coverage is expected for changed paths. At minimum, a unit test mocking Clerk.isInitialized, getCurrentActivity(), and verifying Clerk.attachActivity() is called (or gracefully skipped when null) in both configure() and the ClerkAuthNativeView constructor would prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`
around lines 72 - 162, The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`:
- Around line 72-162: The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: 97a113e5-4d3a-424b-86f0-729ba5c023a0

📥 Commits

Reviewing files that changed from the base of the PR and between 30ec271 and 5aecdf2.

📒 Files selected for processing (4)
  • .changeset/expo-mobile-485-cold-start-activity.md
  • packages/expo/android/build.gradle
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt

@wobsoriano
wobsoriano merged commit 3b74d9a into mainMay 6, 2026
73 of 74 checks passed
@wobsoriano
wobsoriano deleted the chris/mobile-485-cold-start-fix branch May 6, 2026 16:54
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.

2 participants

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

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485) - #8485

Merged
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix
May 6, 2026
Merged

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485)#8485
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Summary

Fixes MOBILE-485.

Customer-reported bug (T-57530, M2X Group, Business tier): on @clerk/expo@3.2.x, the first tap on "Sign in with Google" in <AuthView /> after a cold launch silently fails. Logcat shows:

E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context.

The workaround was to background and foreground the app once before signing in.

Root cause

clerk-android tracks the current foreground Activity via ActivityLifecycleCallbacks registered inside Clerk.initialize(). The callbacks only fire on subsequent onActivityCreated/Started/Resumed events — they don't observe the host Activity's current state at the moment initialize() runs.

In React Native, MainActivity has already passed onResume() by the time <ClerkProvider /> mounts and configure() runs, so when we call Clerk.initialize(), the just-registered callbacks miss the initial Activity. Clerk.currentActivity stays null until the next OS-driven resume cycle — which is why backgrounding and re-foregrounding the app "fixes" it.

Fix

The proper fix lives in clerk-android: it now exposes a public Clerk.attachActivity(activity) API in 1.0.16 for hosts that pass an Application-typed Context to initialize() but have a current Activity available.

This PR adopts that API at two reliable points:

  1. ClerkExpoModule.configure() — right after Clerk.initialize(), calls Clerk.attachActivity(getCurrentActivity()). May be a no-op on cold start if React's host-resume sync hasn't fired yet, but covers the warm-init case.

  2. ClerkAuthNativeView constructor — calls Clerk.attachActivity() with the Activity returned by findActivity(context). This is the empirically-reliable backstop: by the time the AuthView is being constructed, the host Activity is unambiguously available.

Bumps clerk-android-{api,ui} to 1.0.16. Also adds an explicit clerk-android-api dep — without it, telemetry's transitive pin on the prior release wins the version conflict and consumers end up on the older artifact (which doesn't have attachActivity).

Test plan

  • Reproduced the bug first-hand on @clerk/expo@3.2.7 against a Pixel 9 Pro emulator (Android 16). Cold-launch via pm clear + deep-link, tap Google as first action — E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context. in logcat, silent failure on JS, stuck on AuthView.
  • Built @clerk/expo against this branch (consuming clerk-android 1.0.16 via local Maven), reran the same reproduction:
    • I/CredentialManager: starting executeGetCredential[GetGoogleIdOperation] Operation succeeded → Google account picker opens, sign-in completes, lands on the Welcome screen.
    • No MissingActivity in logcat.
  • Re-ran the exact reproduction with the fix in place after a clean pm clear and force-stop — could not reproduce the error.

Gating

This PR depends on clerk/clerk-android#614 shipping as 1.0.16. The build will fail at the clerk_expo:compileDebugKotlin step until that release is published to Maven Central.

…ty (MOBILE-485)
clerk-android tracks the current foreground Activity via
ActivityLifecycleCallbacks registered inside Clerk.initialize(). In a
React Native app, MainActivity has already passed onResume() by the time
<ClerkProvider> mounts and configure() runs, so the callbacks miss the
initial Activity. Without seeding, the first Credential Manager call
(Google sign-in, passkeys) fails with MissingActivity until the user
backgrounds and foregrounds the app.
Adopt the new public Clerk.attachActivity() API from clerk-android 1.0.16
to seed the Activity reference at two reliable points: in
ClerkExpoModule.configure() right after Clerk.initialize() (using
React's currentActivity), and in ClerkAuthNativeView's findActivity()
result. The second hook is the empirically-reliable backstop — at cold
start, getCurrentActivity() may return null before React's host-resume
sync, but ClerkAuthNativeView is constructed when AuthView mounts, by
which point the Activity is unambiguously available.
Bumps clerk-android-{api,ui} to 1.0.16. Adds an explicit clerk-android-api
dep to override telemetry's transitive pin on the previous release —
without this the version constraint loses the conflict and consumers end
up on the older artifact (which lacks attachActivity).
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5aecdf2

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

@vercel

vercelBot commented May 5, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 5, 2026 11:04pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented May 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 5aecdf2

@coderabbitai

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request addresses MissingActivity errors during cold-start sign-in flows by ensuring the Android Activity is registered with the Clerk SDK before credential operations. The changes include: (1) updating Android dependency versions from 1.0.13 to 1.0.16 and adding the clerk-android-api dependency; (2) invoking Clerk.attachActivity() at SDK initialization in ClerkExpoModule and during view initialization in ClerkAuthExpoView; and (3) documenting the patch in a changeset. No public API signatures are modified, and the fix is transparent on rebuild.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically identifies the main fix: seeding the Clerk activity reference on cold-start to resolve the MissingActivity bug, with the issue ticket reference for traceability.
Description check✅ PassedThe description thoroughly explains the customer-reported bug, root cause analysis, the implemented fix across multiple files, dependency bumps, and comprehensive test results—all directly relevant to the changeset.
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.


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt (1)

72-162: ⚡ Quick win

No automated tests cover the cold-start fix

No tests are added or modified in this PR. Per project guidelines, test coverage is expected for changed paths. At minimum, a unit test mocking Clerk.isInitialized, getCurrentActivity(), and verifying Clerk.attachActivity() is called (or gracefully skipped when null) in both configure() and the ClerkAuthNativeView constructor would prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`
around lines 72 - 162, The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`:
- Around line 72-162: The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: 97a113e5-4d3a-424b-86f0-729ba5c023a0

📥 Commits

Reviewing files that changed from the base of the PR and between 30ec271 and 5aecdf2.

📒 Files selected for processing (4)
  • .changeset/expo-mobile-485-cold-start-activity.md
  • packages/expo/android/build.gradle
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt

@wobsoriano
wobsoriano merged commit 3b74d9a into mainMay 6, 2026
73 of 74 checks passed
@wobsoriano
wobsoriano deleted the chris/mobile-485-cold-start-fix branch May 6, 2026 16:54
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.

2 participants

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

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485) - #8485

Merged
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix
May 6, 2026
Merged

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485)#8485
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Summary

Fixes MOBILE-485.

Customer-reported bug (T-57530, M2X Group, Business tier): on @clerk/expo@3.2.x, the first tap on "Sign in with Google" in <AuthView /> after a cold launch silently fails. Logcat shows:

E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context.

The workaround was to background and foreground the app once before signing in.

Root cause

clerk-android tracks the current foreground Activity via ActivityLifecycleCallbacks registered inside Clerk.initialize(). The callbacks only fire on subsequent onActivityCreated/Started/Resumed events — they don't observe the host Activity's current state at the moment initialize() runs.

In React Native, MainActivity has already passed onResume() by the time <ClerkProvider /> mounts and configure() runs, so when we call Clerk.initialize(), the just-registered callbacks miss the initial Activity. Clerk.currentActivity stays null until the next OS-driven resume cycle — which is why backgrounding and re-foregrounding the app "fixes" it.

Fix

The proper fix lives in clerk-android: it now exposes a public Clerk.attachActivity(activity) API in 1.0.16 for hosts that pass an Application-typed Context to initialize() but have a current Activity available.

This PR adopts that API at two reliable points:

  1. ClerkExpoModule.configure() — right after Clerk.initialize(), calls Clerk.attachActivity(getCurrentActivity()). May be a no-op on cold start if React's host-resume sync hasn't fired yet, but covers the warm-init case.

  2. ClerkAuthNativeView constructor — calls Clerk.attachActivity() with the Activity returned by findActivity(context). This is the empirically-reliable backstop: by the time the AuthView is being constructed, the host Activity is unambiguously available.

Bumps clerk-android-{api,ui} to 1.0.16. Also adds an explicit clerk-android-api dep — without it, telemetry's transitive pin on the prior release wins the version conflict and consumers end up on the older artifact (which doesn't have attachActivity).

Test plan

  • Reproduced the bug first-hand on @clerk/expo@3.2.7 against a Pixel 9 Pro emulator (Android 16). Cold-launch via pm clear + deep-link, tap Google as first action — E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context. in logcat, silent failure on JS, stuck on AuthView.
  • Built @clerk/expo against this branch (consuming clerk-android 1.0.16 via local Maven), reran the same reproduction:
    • I/CredentialManager: starting executeGetCredential[GetGoogleIdOperation] Operation succeeded → Google account picker opens, sign-in completes, lands on the Welcome screen.
    • No MissingActivity in logcat.
  • Re-ran the exact reproduction with the fix in place after a clean pm clear and force-stop — could not reproduce the error.

Gating

This PR depends on clerk/clerk-android#614 shipping as 1.0.16. The build will fail at the clerk_expo:compileDebugKotlin step until that release is published to Maven Central.

…ty (MOBILE-485)
clerk-android tracks the current foreground Activity via
ActivityLifecycleCallbacks registered inside Clerk.initialize(). In a
React Native app, MainActivity has already passed onResume() by the time
<ClerkProvider> mounts and configure() runs, so the callbacks miss the
initial Activity. Without seeding, the first Credential Manager call
(Google sign-in, passkeys) fails with MissingActivity until the user
backgrounds and foregrounds the app.
Adopt the new public Clerk.attachActivity() API from clerk-android 1.0.16
to seed the Activity reference at two reliable points: in
ClerkExpoModule.configure() right after Clerk.initialize() (using
React's currentActivity), and in ClerkAuthNativeView's findActivity()
result. The second hook is the empirically-reliable backstop — at cold
start, getCurrentActivity() may return null before React's host-resume
sync, but ClerkAuthNativeView is constructed when AuthView mounts, by
which point the Activity is unambiguously available.
Bumps clerk-android-{api,ui} to 1.0.16. Adds an explicit clerk-android-api
dep to override telemetry's transitive pin on the previous release —
without this the version constraint loses the conflict and consumers end
up on the older artifact (which lacks attachActivity).
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5aecdf2

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

@vercel

vercelBot commented May 5, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 5, 2026 11:04pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented May 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 5aecdf2

@coderabbitai

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request addresses MissingActivity errors during cold-start sign-in flows by ensuring the Android Activity is registered with the Clerk SDK before credential operations. The changes include: (1) updating Android dependency versions from 1.0.13 to 1.0.16 and adding the clerk-android-api dependency; (2) invoking Clerk.attachActivity() at SDK initialization in ClerkExpoModule and during view initialization in ClerkAuthExpoView; and (3) documenting the patch in a changeset. No public API signatures are modified, and the fix is transparent on rebuild.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically identifies the main fix: seeding the Clerk activity reference on cold-start to resolve the MissingActivity bug, with the issue ticket reference for traceability.
Description check✅ PassedThe description thoroughly explains the customer-reported bug, root cause analysis, the implemented fix across multiple files, dependency bumps, and comprehensive test results—all directly relevant to the changeset.
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.


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt (1)

72-162: ⚡ Quick win

No automated tests cover the cold-start fix

No tests are added or modified in this PR. Per project guidelines, test coverage is expected for changed paths. At minimum, a unit test mocking Clerk.isInitialized, getCurrentActivity(), and verifying Clerk.attachActivity() is called (or gracefully skipped when null) in both configure() and the ClerkAuthNativeView constructor would prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`
around lines 72 - 162, The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`:
- Around line 72-162: The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: 97a113e5-4d3a-424b-86f0-729ba5c023a0

📥 Commits

Reviewing files that changed from the base of the PR and between 30ec271 and 5aecdf2.

📒 Files selected for processing (4)
  • .changeset/expo-mobile-485-cold-start-activity.md
  • packages/expo/android/build.gradle
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt

@wobsoriano
wobsoriano merged commit 3b74d9a into mainMay 6, 2026
73 of 74 checks passed
@wobsoriano
wobsoriano deleted the chris/mobile-485-cold-start-fix branch May 6, 2026 16:54
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.

2 participants

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

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485) - #8485

Merged
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix
May 6, 2026
Merged

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485)#8485
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Summary

Fixes MOBILE-485.

Customer-reported bug (T-57530, M2X Group, Business tier): on @clerk/expo@3.2.x, the first tap on "Sign in with Google" in <AuthView /> after a cold launch silently fails. Logcat shows:

E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context.

The workaround was to background and foreground the app once before signing in.

Root cause

clerk-android tracks the current foreground Activity via ActivityLifecycleCallbacks registered inside Clerk.initialize(). The callbacks only fire on subsequent onActivityCreated/Started/Resumed events — they don't observe the host Activity's current state at the moment initialize() runs.

In React Native, MainActivity has already passed onResume() by the time <ClerkProvider /> mounts and configure() runs, so when we call Clerk.initialize(), the just-registered callbacks miss the initial Activity. Clerk.currentActivity stays null until the next OS-driven resume cycle — which is why backgrounding and re-foregrounding the app "fixes" it.

Fix

The proper fix lives in clerk-android: it now exposes a public Clerk.attachActivity(activity) API in 1.0.16 for hosts that pass an Application-typed Context to initialize() but have a current Activity available.

This PR adopts that API at two reliable points:

  1. ClerkExpoModule.configure() — right after Clerk.initialize(), calls Clerk.attachActivity(getCurrentActivity()). May be a no-op on cold start if React's host-resume sync hasn't fired yet, but covers the warm-init case.

  2. ClerkAuthNativeView constructor — calls Clerk.attachActivity() with the Activity returned by findActivity(context). This is the empirically-reliable backstop: by the time the AuthView is being constructed, the host Activity is unambiguously available.

Bumps clerk-android-{api,ui} to 1.0.16. Also adds an explicit clerk-android-api dep — without it, telemetry's transitive pin on the prior release wins the version conflict and consumers end up on the older artifact (which doesn't have attachActivity).

Test plan

  • Reproduced the bug first-hand on @clerk/expo@3.2.7 against a Pixel 9 Pro emulator (Android 16). Cold-launch via pm clear + deep-link, tap Google as first action — E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context. in logcat, silent failure on JS, stuck on AuthView.
  • Built @clerk/expo against this branch (consuming clerk-android 1.0.16 via local Maven), reran the same reproduction:
    • I/CredentialManager: starting executeGetCredential[GetGoogleIdOperation] Operation succeeded → Google account picker opens, sign-in completes, lands on the Welcome screen.
    • No MissingActivity in logcat.
  • Re-ran the exact reproduction with the fix in place after a clean pm clear and force-stop — could not reproduce the error.

Gating

This PR depends on clerk/clerk-android#614 shipping as 1.0.16. The build will fail at the clerk_expo:compileDebugKotlin step until that release is published to Maven Central.

…ty (MOBILE-485)
clerk-android tracks the current foreground Activity via
ActivityLifecycleCallbacks registered inside Clerk.initialize(). In a
React Native app, MainActivity has already passed onResume() by the time
<ClerkProvider> mounts and configure() runs, so the callbacks miss the
initial Activity. Without seeding, the first Credential Manager call
(Google sign-in, passkeys) fails with MissingActivity until the user
backgrounds and foregrounds the app.
Adopt the new public Clerk.attachActivity() API from clerk-android 1.0.16
to seed the Activity reference at two reliable points: in
ClerkExpoModule.configure() right after Clerk.initialize() (using
React's currentActivity), and in ClerkAuthNativeView's findActivity()
result. The second hook is the empirically-reliable backstop — at cold
start, getCurrentActivity() may return null before React's host-resume
sync, but ClerkAuthNativeView is constructed when AuthView mounts, by
which point the Activity is unambiguously available.
Bumps clerk-android-{api,ui} to 1.0.16. Adds an explicit clerk-android-api
dep to override telemetry's transitive pin on the previous release —
without this the version constraint loses the conflict and consumers end
up on the older artifact (which lacks attachActivity).
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5aecdf2

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

@vercel

vercelBot commented May 5, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 5, 2026 11:04pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented May 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 5aecdf2

@coderabbitai

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request addresses MissingActivity errors during cold-start sign-in flows by ensuring the Android Activity is registered with the Clerk SDK before credential operations. The changes include: (1) updating Android dependency versions from 1.0.13 to 1.0.16 and adding the clerk-android-api dependency; (2) invoking Clerk.attachActivity() at SDK initialization in ClerkExpoModule and during view initialization in ClerkAuthExpoView; and (3) documenting the patch in a changeset. No public API signatures are modified, and the fix is transparent on rebuild.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically identifies the main fix: seeding the Clerk activity reference on cold-start to resolve the MissingActivity bug, with the issue ticket reference for traceability.
Description check✅ PassedThe description thoroughly explains the customer-reported bug, root cause analysis, the implemented fix across multiple files, dependency bumps, and comprehensive test results—all directly relevant to the changeset.
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.


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt (1)

72-162: ⚡ Quick win

No automated tests cover the cold-start fix

No tests are added or modified in this PR. Per project guidelines, test coverage is expected for changed paths. At minimum, a unit test mocking Clerk.isInitialized, getCurrentActivity(), and verifying Clerk.attachActivity() is called (or gracefully skipped when null) in both configure() and the ClerkAuthNativeView constructor would prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`
around lines 72 - 162, The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`:
- Around line 72-162: The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: 97a113e5-4d3a-424b-86f0-729ba5c023a0

📥 Commits

Reviewing files that changed from the base of the PR and between 30ec271 and 5aecdf2.

📒 Files selected for processing (4)
  • .changeset/expo-mobile-485-cold-start-activity.md
  • packages/expo/android/build.gradle
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt

@wobsoriano
wobsoriano merged commit 3b74d9a into mainMay 6, 2026
73 of 74 checks passed
@wobsoriano
wobsoriano deleted the chris/mobile-485-cold-start-fix branch May 6, 2026 16:54
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.

2 participants

@chriscanin@wobsoriano
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485) - #8485

Merged
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix
May 6, 2026
Merged

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485)#8485
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Summary

Fixes MOBILE-485.

Customer-reported bug (T-57530, M2X Group, Business tier): on @clerk/expo@3.2.x, the first tap on "Sign in with Google" in <AuthView /> after a cold launch silently fails. Logcat shows:

E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context.

The workaround was to background and foreground the app once before signing in.

Root cause

clerk-android tracks the current foreground Activity via ActivityLifecycleCallbacks registered inside Clerk.initialize(). The callbacks only fire on subsequent onActivityCreated/Started/Resumed events — they don't observe the host Activity's current state at the moment initialize() runs.

In React Native, MainActivity has already passed onResume() by the time <ClerkProvider /> mounts and configure() runs, so when we call Clerk.initialize(), the just-registered callbacks miss the initial Activity. Clerk.currentActivity stays null until the next OS-driven resume cycle — which is why backgrounding and re-foregrounding the app "fixes" it.

Fix

The proper fix lives in clerk-android: it now exposes a public Clerk.attachActivity(activity) API in 1.0.16 for hosts that pass an Application-typed Context to initialize() but have a current Activity available.

This PR adopts that API at two reliable points:

  1. ClerkExpoModule.configure() — right after Clerk.initialize(), calls Clerk.attachActivity(getCurrentActivity()). May be a no-op on cold start if React's host-resume sync hasn't fired yet, but covers the warm-init case.

  2. ClerkAuthNativeView constructor — calls Clerk.attachActivity() with the Activity returned by findActivity(context). This is the empirically-reliable backstop: by the time the AuthView is being constructed, the host Activity is unambiguously available.

Bumps clerk-android-{api,ui} to 1.0.16. Also adds an explicit clerk-android-api dep — without it, telemetry's transitive pin on the prior release wins the version conflict and consumers end up on the older artifact (which doesn't have attachActivity).

Test plan

  • Reproduced the bug first-hand on @clerk/expo@3.2.7 against a Pixel 9 Pro emulator (Android 16). Cold-launch via pm clear + deep-link, tap Google as first action — E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context. in logcat, silent failure on JS, stuck on AuthView.
  • Built @clerk/expo against this branch (consuming clerk-android 1.0.16 via local Maven), reran the same reproduction:
    • I/CredentialManager: starting executeGetCredential[GetGoogleIdOperation] Operation succeeded → Google account picker opens, sign-in completes, lands on the Welcome screen.
    • No MissingActivity in logcat.
  • Re-ran the exact reproduction with the fix in place after a clean pm clear and force-stop — could not reproduce the error.

Gating

This PR depends on clerk/clerk-android#614 shipping as 1.0.16. The build will fail at the clerk_expo:compileDebugKotlin step until that release is published to Maven Central.

…ty (MOBILE-485)
clerk-android tracks the current foreground Activity via
ActivityLifecycleCallbacks registered inside Clerk.initialize(). In a
React Native app, MainActivity has already passed onResume() by the time
<ClerkProvider> mounts and configure() runs, so the callbacks miss the
initial Activity. Without seeding, the first Credential Manager call
(Google sign-in, passkeys) fails with MissingActivity until the user
backgrounds and foregrounds the app.
Adopt the new public Clerk.attachActivity() API from clerk-android 1.0.16
to seed the Activity reference at two reliable points: in
ClerkExpoModule.configure() right after Clerk.initialize() (using
React's currentActivity), and in ClerkAuthNativeView's findActivity()
result. The second hook is the empirically-reliable backstop — at cold
start, getCurrentActivity() may return null before React's host-resume
sync, but ClerkAuthNativeView is constructed when AuthView mounts, by
which point the Activity is unambiguously available.
Bumps clerk-android-{api,ui} to 1.0.16. Adds an explicit clerk-android-api
dep to override telemetry's transitive pin on the previous release —
without this the version constraint loses the conflict and consumers end
up on the older artifact (which lacks attachActivity).
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5aecdf2

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

@vercel

vercelBot commented May 5, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 5, 2026 11:04pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented May 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 5aecdf2

@coderabbitai

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request addresses MissingActivity errors during cold-start sign-in flows by ensuring the Android Activity is registered with the Clerk SDK before credential operations. The changes include: (1) updating Android dependency versions from 1.0.13 to 1.0.16 and adding the clerk-android-api dependency; (2) invoking Clerk.attachActivity() at SDK initialization in ClerkExpoModule and during view initialization in ClerkAuthExpoView; and (3) documenting the patch in a changeset. No public API signatures are modified, and the fix is transparent on rebuild.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically identifies the main fix: seeding the Clerk activity reference on cold-start to resolve the MissingActivity bug, with the issue ticket reference for traceability.
Description check✅ PassedThe description thoroughly explains the customer-reported bug, root cause analysis, the implemented fix across multiple files, dependency bumps, and comprehensive test results—all directly relevant to the changeset.
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.


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt (1)

72-162: ⚡ Quick win

No automated tests cover the cold-start fix

No tests are added or modified in this PR. Per project guidelines, test coverage is expected for changed paths. At minimum, a unit test mocking Clerk.isInitialized, getCurrentActivity(), and verifying Clerk.attachActivity() is called (or gracefully skipped when null) in both configure() and the ClerkAuthNativeView constructor would prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`
around lines 72 - 162, The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`:
- Around line 72-162: The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: 97a113e5-4d3a-424b-86f0-729ba5c023a0

📥 Commits

Reviewing files that changed from the base of the PR and between 30ec271 and 5aecdf2.

📒 Files selected for processing (4)
  • .changeset/expo-mobile-485-cold-start-activity.md
  • packages/expo/android/build.gradle
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt

@wobsoriano
wobsoriano merged commit 3b74d9a into mainMay 6, 2026
73 of 74 checks passed
@wobsoriano
wobsoriano deleted the chris/mobile-485-cold-start-fix branch May 6, 2026 16:54
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.

2 participants

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

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485) - #8485

Merged
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix
May 6, 2026
Merged

fix(expo): seed Clerk activity ref on cold-start to fix MissingActivity (MOBILE-485)#8485
wobsoriano merged 1 commit into
mainfrom
chris/mobile-485-cold-start-fix

Conversation

@chriscanin

Copy link
Copy Markdown
Contributor

Summary

Fixes MOBILE-485.

Customer-reported bug (T-57530, M2X Group, Business tier): on @clerk/expo@3.2.x, the first tap on "Sign in with Google" in <AuthView /> after a cold launch silently fails. Logcat shows:

E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context.

The workaround was to background and foreground the app once before signing in.

Root cause

clerk-android tracks the current foreground Activity via ActivityLifecycleCallbacks registered inside Clerk.initialize(). The callbacks only fire on subsequent onActivityCreated/Started/Resumed events — they don't observe the host Activity's current state at the moment initialize() runs.

In React Native, MainActivity has already passed onResume() by the time <ClerkProvider /> mounts and configure() runs, so when we call Clerk.initialize(), the just-registered callbacks miss the initial Activity. Clerk.currentActivity stays null until the next OS-driven resume cycle — which is why backgrounding and re-foregrounding the app "fixes" it.

Fix

The proper fix lives in clerk-android: it now exposes a public Clerk.attachActivity(activity) API in 1.0.16 for hosts that pass an Application-typed Context to initialize() but have a current Activity available.

This PR adopts that API at two reliable points:

  1. ClerkExpoModule.configure() — right after Clerk.initialize(), calls Clerk.attachActivity(getCurrentActivity()). May be a no-op on cold start if React's host-resume sync hasn't fired yet, but covers the warm-init case.

  2. ClerkAuthNativeView constructor — calls Clerk.attachActivity() with the Activity returned by findActivity(context). This is the empirically-reliable backstop: by the time the AuthView is being constructed, the host Activity is unambiguously available.

Bumps clerk-android-{api,ui} to 1.0.16. Also adds an explicit clerk-android-api dep — without it, telemetry's transitive pin on the prior release wins the version conflict and consumers end up on the older artifact (which doesn't have attachActivity).

Test plan

  • Reproduced the bug first-hand on @clerk/expo@3.2.7 against a Pixel 9 Pro emulator (Android 16). Cold-launch via pm clear + deep-link, tap Google as first action — E/ClerkLog: Clerk error: Google sign-in cannot start: Credential Manager requires an active Activity context. in logcat, silent failure on JS, stuck on AuthView.
  • Built @clerk/expo against this branch (consuming clerk-android 1.0.16 via local Maven), reran the same reproduction:
    • I/CredentialManager: starting executeGetCredential[GetGoogleIdOperation] Operation succeeded → Google account picker opens, sign-in completes, lands on the Welcome screen.
    • No MissingActivity in logcat.
  • Re-ran the exact reproduction with the fix in place after a clean pm clear and force-stop — could not reproduce the error.

Gating

This PR depends on clerk/clerk-android#614 shipping as 1.0.16. The build will fail at the clerk_expo:compileDebugKotlin step until that release is published to Maven Central.

…ty (MOBILE-485)
clerk-android tracks the current foreground Activity via
ActivityLifecycleCallbacks registered inside Clerk.initialize(). In a
React Native app, MainActivity has already passed onResume() by the time
<ClerkProvider> mounts and configure() runs, so the callbacks miss the
initial Activity. Without seeding, the first Credential Manager call
(Google sign-in, passkeys) fails with MissingActivity until the user
backgrounds and foregrounds the app.
Adopt the new public Clerk.attachActivity() API from clerk-android 1.0.16
to seed the Activity reference at two reliable points: in
ClerkExpoModule.configure() right after Clerk.initialize() (using
React's currentActivity), and in ClerkAuthNativeView's findActivity()
result. The second hook is the empirically-reliable backstop — at cold
start, getCurrentActivity() may return null before React's host-resume
sync, but ClerkAuthNativeView is constructed when AuthView mounts, by
which point the Activity is unambiguously available.
Bumps clerk-android-{api,ui} to 1.0.16. Adds an explicit clerk-android-api
dep to override telemetry's transitive pin on the previous release —
without this the version constraint loses the conflict and consumers end
up on the older artifact (which lacks attachActivity).
@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5aecdf2

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

@vercel

vercelBot commented May 5, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 5, 2026 11:04pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented May 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 5aecdf2

@coderabbitai

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request addresses MissingActivity errors during cold-start sign-in flows by ensuring the Android Activity is registered with the Clerk SDK before credential operations. The changes include: (1) updating Android dependency versions from 1.0.13 to 1.0.16 and adding the clerk-android-api dependency; (2) invoking Clerk.attachActivity() at SDK initialization in ClerkExpoModule and during view initialization in ClerkAuthExpoView; and (3) documenting the patch in a changeset. No public API signatures are modified, and the fix is transparent on rebuild.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically identifies the main fix: seeding the Clerk activity reference on cold-start to resolve the MissingActivity bug, with the issue ticket reference for traceability.
Description check✅ PassedThe description thoroughly explains the customer-reported bug, root cause analysis, the implemented fix across multiple files, dependency bumps, and comprehensive test results—all directly relevant to the changeset.
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.


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.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt (1)

72-162: ⚡ Quick win

No automated tests cover the cold-start fix

No tests are added or modified in this PR. Per project guidelines, test coverage is expected for changed paths. At minimum, a unit test mocking Clerk.isInitialized, getCurrentActivity(), and verifying Clerk.attachActivity() is called (or gracefully skipped when null) in both configure() and the ClerkAuthNativeView constructor would prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`
around lines 72 - 162, The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt`:
- Around line 72-162: The PR lacks tests for the cold-start activity attach
logic: add unit tests that mock Clerk.isInitialized, getCurrentActivity(), and
Clerk.attachActivity() to cover the new behavior in configure() and the
ClerkAuthNativeView constructor; specifically, create tests that (1) simulate
cold start when Clerk.isInitialized is false and getCurrentActivity() returns a
non-null Activity and assert Clerk.attachActivity(activity) is invoked, (2)
simulate cold start with getCurrentActivity() returning null and assert no
attachActivity call is made (graceful skip), and (3) cover the
already-initialized path where configure() updates the bearer token (mock
Clerk.updateDeviceToken and Clerk.sessionFlow) to ensure session wait logic runs
without attaching activity. Ensure tests stub/restore Clerk.isInitialized,
Clerk.initializationError, Clerk.sessionFlow, getCurrentActivity(), and
Clerk.attachActivity() so they deterministically exercise both branches.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: 97a113e5-4d3a-424b-86f0-729ba5c023a0

📥 Commits

Reviewing files that changed from the base of the PR and between 30ec271 and 5aecdf2.

📒 Files selected for processing (4)
  • .changeset/expo-mobile-485-cold-start-activity.md
  • packages/expo/android/build.gradle
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt

@wobsoriano
wobsoriano merged commit 3b74d9a into mainMay 6, 2026
73 of 74 checks passed
@wobsoriano
wobsoriano deleted the chris/mobile-485-cold-start-fix branch May 6, 2026 16:54
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.

2 participants

@chriscanin@wobsoriano