microg/GmsCore · error · IllegalArgumentException
invalid handle
Error message
invalid handle
What it means
RecaptchaGuardImpl.execute() validates that the RecaptchaHandle passed in belongs to the calling app. If the handle carries a non-null clientPackageName that differs from the package name this service instance was created for, it throws IllegalArgumentException("invalid handle"). The handle is bound to the app that called init(); handles cannot be reused across apps or processes.
Source
Thrown at play-services-recaptcha/core/src/main/kotlin/org/microg/gms/recaptcha/RecaptchaGuardImpl.kt:48
class RecaptchaGuardImpl(private val context: Context, private val packageName: String) : RecaptchaImpl {
private val queue = singleInstanceOf { Volley.newRequestQueue(context.applicationContext) }
private var lastToken: String? = null
override suspend fun init(params: InitParams): RecaptchaHandle {
val response = ProtobufPostRequest(
"https://www.recaptcha.net/recaptcha/api3/ac", RecaptchaInitRequest(
data_ = RecaptchaInitRequest.Data(
siteKey = params.siteKey,
packageName = packageName,
version = "${VersionUtil(context).versionCode};${params.version}"
)
), RecaptchaInitResponse.ADAPTER
).sendAndAwait(queue)
lastToken = response.token
return RecaptchaHandle(params.siteKey, packageName, response.acceptableAdditionalArgs.toList())
}
override suspend fun execute(params: ExecuteParams): RecaptchaResultData {
if (params.handle.clientPackageName != null && params.handle.clientPackageName != packageName) throw IllegalArgumentException("invalid handle")
val timestamp = System.currentTimeMillis()
val additionalArgs = mutableMapOf<String, String>()
val guardMap = mutableMapOf<String, String>()
for (key in params.action.additionalArgs.keySet()) {
val value = params.action.additionalArgs.getString(key)
?: throw Exception("Only string values are allowed as an additional arg in RecaptchaAction")
if (key !in params.handle.acceptableAdditionalArgs)
throw Exception("AdditionalArgs key[ \"$key\" ] is not accepted by reCATPCHA server")
additionalArgs.put(key, value)
}
Log.d(TAG, "Additional arguments: $additionalArgs")
if (lastToken == null) {
init(InitParams().apply { siteKey = params.handle.siteKey; version = params.version })
}
val token = lastToken!!
guardMap["token"] = token
guardMap["action"] = params.action.toString()View on GitHub (pinned to 157c9d86ac)
Solutions
- Ensure the handle used in execute() came from init() in the same app/process with the same package name
- Check that RecaptchaHandle.clientPackageName is null or equals your packageName before calling execute
- Re-call init() to obtain a fresh handle for the current app
- Verify no code is deserializing an old handle from storage with a wrong or legacy package name
Example fix
// before
val result = guard.execute(executeParams) // executeParams.handle.clientPackageName = "com.other.app"
// after
if (executeParams.handle.clientPackageName == null || executeParams.handle.clientPackageName == packageName) {
val result = guard.execute(executeParams)
} else {
val handle = guard.init(initParams) // fresh handle for this app
val result = guard.execute(executeParams.copy(handle = handle))
} Defensive patterns
Strategy: validation
Validate before calling
require(handle.clientPackageName == null || handle.clientPackageName == packageName) { "handle not owned by this app" } Type guard
fun RecaptchaHandle.isOwnedBy(pkg: String): Boolean = clientPackageName == null || clientPackageName == pkg
Try / catch
try { guard.execute(params) } catch (e: IllegalArgumentException) { /* re-init handle */ } Prevention
- Always obtain handles from init() in the same process
- Never persist and reuse handles across installs
- Null clientPackageName in test fixtures
When it happens
Trigger: Calling execute() with a handle whose clientPackageName field is set and does not equal the packageName of the current RecaptchaGuardImpl — e.g. a handle obtained by another app, a stale/parceled handle from a different uid, or a hand-crafted handle in tests.
Common situations: App passes a handle received from a different Google Play services account/process, reuses a serialized handle after app reinstall (package identity check fails), or a test constructs RecaptchaHandle with the wrong clientPackageName.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- invalid handle
- INTERNAL_ERROR
- Missing package name
- maxUpdates must be greater than 0
- minUpdateDistanceMeters must be greater than or equal to 0
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/bdc3dea07c0c3116.
Report an issue: GitHub.