diff --git a/.changeset/expo-mobile-485-cold-start-activity.md b/.changeset/expo-mobile-485-cold-start-activity.md
new file mode 100644
index 00000000000..554e999358d
--- /dev/null
+++ b/.changeset/expo-mobile-485-cold-start-activity.md
@@ -0,0 +1,7 @@
+---
+"@clerk/expo": patch
+---
+
+Fix `MissingActivity` error on cold-start Google sign-in / passkey flows. Previously, the first tap on "Sign in with Google" in `` failed with `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.
+
+The Android bridge now calls `Clerk.attachActivity()` (added in clerk-android 1.0.16) at SDK initialization and on AuthView/UserProfile mount, so the current Activity is registered with the underlying SDK before any Credential Manager call. No app-side changes required; the fix is transparent on rebuild.
diff --git a/packages/expo/android/build.gradle b/packages/expo/android/build.gradle
index bb501a1aa93..d860cd02919 100644
--- a/packages/expo/android/build.gradle
+++ b/packages/expo/android/build.gradle
@@ -18,8 +18,8 @@ ext {
credentialsVersion = "1.3.0"
googleIdVersion = "1.1.1"
kotlinxCoroutinesVersion = "1.7.3"
- clerkAndroidApiVersion = "1.0.13"
- clerkAndroidUiVersion = "1.0.13"
+ clerkAndroidApiVersion = "1.0.16"
+ clerkAndroidUiVersion = "1.0.16"
composeVersion = "1.7.0"
activityComposeVersion = "1.9.0"
lifecycleVersion = "2.8.0"
@@ -117,6 +117,16 @@ dependencies {
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
exclude group: 'com.squareup.okhttp3', module: 'okhttp-urlconnection'
}
+ // clerk-android-telemetry has a transitive dep on the last released
+ // clerk-android-api. Pinning the api explicitly here keeps consumers
+ // compiling against the same version we ship the UI from.
+ implementation("com.clerk:clerk-android-api:$clerkAndroidApiVersion") {
+ exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib'
+ exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk7'
+ exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
+ exclude group: 'com.squareup.okhttp3', module: 'okhttp'
+ exclude group: 'com.squareup.okhttp3', module: 'okhttp-urlconnection'
+ }
// Jetpack Compose for wrapping Clerk views
implementation "androidx.compose.ui:ui:$composeVersion"
diff --git a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt b/packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt
index 8d3b1ed0100..ce948f7a8a4 100644
--- a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt
+++ b/packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt
@@ -44,7 +44,14 @@ class ClerkAuthNativeView(context: Context) : FrameLayout(context) {
var mode: String = "signInOrUp"
var isDismissable: Boolean = true
- private val activity: ComponentActivity? = findActivity(context)
+ private val activity: ComponentActivity? = findActivity(context).also {
+ // At cold start, ClerkExpoModule.configure() may run before React's
+ // host-resume sync — meaning getCurrentActivity() returns null there.
+ // This view's construction is a reliable second hook: we know the Activity
+ // is available (we just walked the context to find it) and we're about to
+ // render Google sign-in / passkey buttons that need it.
+ if (it != null) Clerk.attachActivity(it)
+ }
// Per-view ViewModelStoreOwner so the AuthView's ViewModels (including its
// navigation state) are scoped to THIS view instance, not the activity.
diff --git a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt b/packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt
index 1ab29a4ab0f..7c822cfda40 100644
--- a/packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt
+++ b/packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt
@@ -85,6 +85,16 @@ class ClerkExpoModule(reactContext: ReactApplicationContext) :
}
Clerk.initialize(reactApplicationContext, pubKey)
+ // clerk-android registers ActivityLifecycleCallbacks during
+ // initialize(), but in React Native MainActivity has already passed
+ // onResume() by the time mounts and we reach this
+ // line, 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. getCurrentActivity() can be null here on
+ // cold start before React's host-resume sync — AuthView and
+ // UserProfile also call attachActivity() on mount as a backstop.
+ getCurrentActivity()?.let { Clerk.attachActivity(it) }
// Theme loading is centralized here. ClerkViewFactory.configure()
// and ClerkUserProfileActivity.onCreate() only call Clerk.initialize()
// when Clerk is not yet initialized, so by the time they run