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

  1. Use only handles returned by init() from the same RecaptchaWebImpl instance
  2. Null out or correctly set clientPackageName when constructing handles in tests
  3. Re-init to obtain a fresh valid handle instead of reusing persisted ones
  4. 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

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


AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06). Data as JSON: /api/errors/a9f221415a5e02b8. Report an issue: GitHub.