Skip to content

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

Merged
wobsoriano merged 1 commit intomainfrom
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 intomainfrom
chris/mobile-485-cold-start-fix

Conversation

@chriscanin
Copy link
Copy Markdown
Member

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-bot Bot commented May 5, 2026

🦋 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
Name Type
@clerk/expo Patch

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
Copy link
Copy Markdown

vercel Bot commented May 5, 2026

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

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment May 5, 2026 11:04pm

Request Review

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 5, 2026

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

coderabbitai Bot commented May 5, 2026

📝 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 name Status Explanation
Title check ✅ Passed The 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 ✅ Passed The 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 ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check 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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 main May 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 free to 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