microg/GmsCore · error · IllegalArgumentException
invalid handle
Error message
invalid handle
What it means
RecaptchaWebImpl.close() enforces handle ownership: if handle.clientPackageName is non-null and differs from the packageName this web-based reCAPTCHA implementation serves, it throws IllegalArgumentException("invalid handle"). It prevents closing a WebView-based reCAPTCHA session belonging to a different app.
Source
Thrown at play-services-recaptcha/core/src/main/kotlin/org/microg/gms/recaptcha/RecaptchaWebImpl.kt:151
val request = RecaptchaExecuteRequest(token = lastRequestToken, action = params.action.toString(), additionalArgs = additionalArgs).encode().toBase64(Base64.URL_SAFE, Base64.NO_WRAP)
val token = suspendCoroutine { continuation ->
executeFinished.set(false)
executeContinuation = continuation
eval("recaptcha.m.Main.execute(\"${request}\")")
lifecycleScope.launch {
delay(10000)
if (!executeFinished.getAndSet(true)) {
try {
continuation.resumeWithException(RuntimeException("Timeout reached"))
} catch (_: Exception) {}
}
}
}
return RecaptchaResultData(token)
}
override suspend fun close(handle: RecaptchaHandle): Boolean {
if (handle.clientPackageName != null && handle.clientPackageName != packageName) throw IllegalArgumentException("invalid handle")
val closed = webView != null
webView?.stopLoading()
webView?.loadUrl("about:blank")
webView = null
return closed
}
private fun eval(script: String) {
Log.d(TAG, "eval: $script")
webView?.let {
Handler(context.mainLooper).post {
it.evaluateJavascript(script, null)
}
}
}
protected fun finalize() {
FakeApplication.packageNameOverride = ""View on GitHub (pinned to 157c9d86ac)
Solutions
- Use only handles returned by init() from the same RecaptchaWebImpl instance
- Null out or correctly set clientPackageName when constructing handles in tests
- Re-init to obtain a fresh valid handle instead of reusing persisted ones
- Verify the app's packageName matches what was passed when the service was created
Example fix
// before webImpl.close(handle) // handle.clientPackageName = "com.other" // after check(handle.clientPackageName == null || handle.clientPackageName == packageName) webImpl.close(handle)
Defensive patterns
Strategy: validation
Validate before calling
require(handle.clientPackageName == null || handle.clientPackageName == packageName)
Type guard
fun RecaptchaHandle.belongsTo(pkg: String) = clientPackageName == null || clientPackageName == pkg
Try / catch
try { webImpl.close(handle) } catch (e: IllegalArgumentException) { webImpl.close(webImpl.init(params)) } Prevention
- Keep handle and impl instance paired
- Avoid persisting web handles across sessions
- Correct clientPackageName in tests
When it happens
Trigger: Calling close(handle) with a handle whose clientPackageName points at another package — cross-app handle reuse, restored/stale handles, or wrong package in test-constructed handles.
Common situations: Web-view reCAPTCHA handles shared across activities running under different package identities, handles persisted and reused after the app was renamed, or instrumentation tests passing mismatched package names.
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/a9f221415a5e02b8.
Report an issue: GitHub.