{"record":{"id":"73e439af3baa1beb","repo":"microg/GmsCore","slug":"network-url-required","errorCode":null,"errorMessage":"Network URL required","messagePattern":"Network URL required","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"play-services-droidguard/core/src/main/kotlin/org/microg/gms/droidguard/core/RemoteHandleImpl.kt","lineNumber":24,"sourceCode":"package org.microg.gms.droidguard.core\n\nimport android.content.Context\nimport android.net.Uri\nimport android.util.Base64\nimport com.google.android.gms.droidguard.internal.DroidGuardInitReply\nimport com.google.android.gms.droidguard.internal.DroidGuardResultsRequest\nimport com.google.android.gms.droidguard.internal.IDroidGuardHandle\nimport android.util.Log\nimport java.net.HttpURLConnection\nimport java.net.URL\n\nprivate const val TAG = \"RemoteGuardImpl\"\n\nclass RemoteHandleImpl(private val context: Context, private val packageName: String) : IDroidGuardHandle.Stub() {\n    private var flow: String? = null\n    private var request: DroidGuardResultsRequest? = null\n    private val url: String\n        get() = DroidGuardPreferences.getNetworkServerUrl(context) ?: throw IllegalStateException(\"Network URL required\")\n\n    override fun init(flow: String?) {\n        Log.d(TAG, \"init($flow)\")\n        this.flow = flow\n    }\n\n    override fun snapshot(map: Map<Any?, Any?>?): ByteArray {\n        Log.d(TAG, \"snapshot($map)\")\n        val paramsMap = mutableMapOf(\"flow\" to flow, \"source\" to packageName)\n        for (key in request?.bundle?.keySet().orEmpty()) {\n            request?.bundle?.getString(key)?.let { paramsMap[\"x-request-$key\"] = it }\n        }\n        val params = paramsMap.map { Uri.encode(it.key) + \"=\" + Uri.encode(it.value) }.joinToString(\"&\")\n        val connection = URL(\"$url?$params\").openConnection() as HttpURLConnection\n        val payload = map.orEmpty().map { Uri.encode(it.key as String) + \"=\" + Uri.encode(it.value as String) }.joinToString(\"&\")\n        Log.d(TAG, \"POST ${connection.url}: $payload\")\n        connection.setRequestProperty(\"Content-Type\", \"application/x-www-form-urlencoded; charset=UTF-8\")\n        connection.requestMethod = \"POST\"","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-droidguard/core/src/main/kotlin/org/microg/gms/droidguard/core/RemoteHandleImpl.kt#L6-L42","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Configure the DroidGuard network server URL in microG settings (or via the DroidGuardPreferences setter) before using RemoteHandleImpl","Check DroidGuardPreferences.getNetworkServerUrl(context) for null before constructing/using RemoteHandleImpl and choose a local handle instead","Catch IllegalStateException around handle usage and prompt the user to configure the server URL","If the deployment is local-only, stop instantiating RemoteHandleImpl and use NetworkHandleProxyFactory paths exclusively"],"exampleFix":"// before\nval handle = RemoteHandleImpl(context, packageName)\nhandle.init(flow) // throws if no URL configured\n\n// after\nval serverUrl = DroidGuardPreferences.getNetworkServerUrl(context)\nrequireNotNull(serverUrl) { \"Configure a DroidGuard network server URL first\" }\nval handle = RemoteHandleImpl(context, packageName)\nhandle.init(flow)","handlingStrategy":"validation","validationCode":"val serverUrl = DroidGuardPreferences.getNetworkServerUrl(context)\nif (serverUrl == null) {\n    configureServerUrlOrDefault() // or use a local handle\n} else {\n    RemoteHandleImpl(context, packageName).init(flow)\n}","typeGuard":null,"tryCatchPattern":"try {\n    remoteHandle.init(flow)\n} catch (e: IllegalStateException) {\n    promptUserToConfigureDroidGuardServer()\n}","preventionTips":["Configure the network server URL before instantiating RemoteHandleImpl","Persist the setting so microG data resets don't silently drop it","For local-only deployments, never construct the remote handle"],"tags":["android","microg","droidguard","configuration","network"],"backgroundTag":"missing-config-value","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}