microg/GmsCore · error · IllegalStateException

Network URL required

Error message

Network URL required

What it means

RemoteHandleImpl performs DroidGuard flows against a remote network server whose URL is read lazily from DroidGuardPreferences.getNetworkServerUrl(context). When no server URL is configured, the url property getter throws IllegalStateException('Network URL required') as soon as any operation needs the URL. The remote handle is useless without a configured endpoint, so the library fails fast instead of issuing a request to null.

Solutions

  1. Configure the DroidGuard network server URL in microG settings (or via the DroidGuardPreferences setter) before using RemoteHandleImpl
  2. Check DroidGuardPreferences.getNetworkServerUrl(context) for null before constructing/using RemoteHandleImpl and choose a local handle instead
  3. Catch IllegalStateException around handle usage and prompt the user to configure the server URL
  4. If the deployment is local-only, stop instantiating RemoteHandleImpl and use NetworkHandleProxyFactory paths exclusively

Example fix

// before
val handle = RemoteHandleImpl(context, packageName)
handle.init(flow) // throws if no URL configured

// after
val serverUrl = DroidGuardPreferences.getNetworkServerUrl(context)
requireNotNull(serverUrl) { "Configure a DroidGuard network server URL first" }
val handle = RemoteHandleImpl(context, packageName)
handle.init(flow)
Defensive patterns

Strategy: validation

Validate before calling

val serverUrl = DroidGuardPreferences.getNetworkServerUrl(context)
if (serverUrl == null) {
    configureServerUrlOrDefault() // or use a local handle
} else {
    RemoteHandleImpl(context, packageName).init(flow)
}

Try / catch

try {
    remoteHandle.init(flow)
} catch (e: IllegalStateException) {
    promptUserToConfigureDroidGuardServer()
}

Prevention

When it happens

Trigger: Creating a RemoteHandleImpl(context, packageName) and calling init/sendReport/etc. on an install where DroidGuardPreferences.getNetworkServerUrl(context) returns null — i.e. no DroidGuard network server URL has ever been set in preferences.

Common situations: Users who disabled or never configured the DroidGuard network server in microG settings; resetting microG data wipes the configured URL; deployments using only local DroidGuard attempting a remote handle; fork builds missing default server URL configuration.

Understand the failure class

Background: "missing required config value" errors: why libraries refuse to start when a configuration key is empty, unset, or blank — this error's family across 48 libraries.

Related errors


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

Appendix: source

Thrown at play-services-droidguard/core/src/main/kotlin/org/microg/gms/droidguard/core/RemoteHandleImpl.kt:24

package org.microg.gms.droidguard.core

import android.content.Context
import android.net.Uri
import android.util.Base64
import com.google.android.gms.droidguard.internal.DroidGuardInitReply
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
import com.google.android.gms.droidguard.internal.IDroidGuardHandle
import android.util.Log
import java.net.HttpURLConnection
import java.net.URL

private const val TAG = "RemoteGuardImpl"

class RemoteHandleImpl(private val context: Context, private val packageName: String) : IDroidGuardHandle.Stub() {
    private var flow: String? = null
    private var request: DroidGuardResultsRequest? = null
    private val url: String
        get() = DroidGuardPreferences.getNetworkServerUrl(context) ?: throw IllegalStateException("Network URL required")

    override fun init(flow: String?) {
        Log.d(TAG, "init($flow)")
        this.flow = flow
    }

    override fun snapshot(map: Map<Any?, Any?>?): ByteArray {
        Log.d(TAG, "snapshot($map)")
        val paramsMap = mutableMapOf("flow" to flow, "source" to packageName)
        for (key in request?.bundle?.keySet().orEmpty()) {
            request?.bundle?.getString(key)?.let { paramsMap["x-request-$key"] = it }
        }
        val params = paramsMap.map { Uri.encode(it.key) + "=" + Uri.encode(it.value) }.joinToString("&")
        val connection = URL("$url?$params").openConnection() as HttpURLConnection
        val payload = map.orEmpty().map { Uri.encode(it.key as String) + "=" + Uri.encode(it.value as String) }.joinToString("&")
        Log.d(TAG, "POST ${connection.url}: $payload")
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
        connection.requestMethod = "POST"

View on GitHub (pinned to 157c9d86ac)