microg/GmsCore · error · RuntimeException

Lack of permission

Error message

Lack of permission

What it means

LocationRequestManager.LocationRequestHolder's init/update path throws RuntimeException('Lack of permission') when the app-op check for the request's effective granularity fails — the client does not hold the app-op (runtime appop mode denied) required for the granularity (e.g. precise/coarse) it requested. Unlike SecurityException paths, this surfaces as a RuntimeException inside request registration.

Source

Thrown at play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt:436

                get() = request.intervalMillis
            val updatesPending: Int
                get() = request.maxUpdates - updates
            val timePendingMillis: Long
                get() = request.durationMillis - (SystemClock.elapsedRealtime() - start)
            var workSource: WorkSource = WorkSource(request.workSource).also { if (!clientIdentity.isSelfUser()) WorkSourceUtil.add(it, clientIdentity.uid, clientIdentity.packageName) }
                private set
            val effectiveHighPower: Boolean
                get() = request.intervalMillis < 60000 || effectivePriority == PRIORITY_HIGH_ACCURACY

            fun update(callback: ILocationCallback, request: LocationRequest): LocationRequestHolder {
                val changedGranularity = request.granularity != this.request.granularity || request.granularity == GRANULARITY_PERMISSION_LEVEL
                this.callback = callback
                this.request = request
                this.start = SystemClock.elapsedRealtime()
                this.updates = 0
                this.workSource = WorkSource(request.workSource).also { if (!clientIdentity.isSelfUser()) WorkSourceUtil.add(it, clientIdentity.uid, clientIdentity.packageName) }
                if (changedGranularity) {
                    if (!context.checkAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) throw RuntimeException("Lack of permission")
                }
                return this
            }

            fun start(): LocationRequestHolder {
                if (!context.checkAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) throw RuntimeException("Lack of permission")
                return this
            }

            fun cancel() {
                try {
                    callback?.cancel()
                } catch (e: Exception) {
                    Log.w(TAG, e)
                }
            }

            fun check() {

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Re-grant the location permission/app-op: Settings > Apps > Permissions > Location, or adb shell appops set <pkg> ACCESS_FINE_LOCATION allow.
  2. Lower the requested granularity (use COARSE granularity) to match the permission actually held.
  3. Before requesting updates, verify checkSelfPermission and the corresponding app-op mode for fine location.

Example fix

// before
request = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 1000).build() // needs fine app-op
// after
// use coarse when only coarse is granted
request = LocationRequest.Builder(Priority.PRIORITY_BALANCED_POWER_ACCURACY, 1000)
    .setGranularity(Granularity.GRANULARITY_COARSE)
    .build()
Defensive patterns

Strategy: validation

Validate before calling

val fine = ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PERMISSION_GRANTED
val coarse = ContextCompat.checkSelfPermission(context, ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED
val granularity = if (fine) Granularity.GRANULARITY_FINE else if (coarse) Granularity.GRANULARITY_COARSE else return

Type guard

fun granularityAllowed(ctx: Context, want: Int): Boolean =
    when (want) {
        Granularity.GRANULARITY_FINE -> ctx.checkSelfPermission(ACCESS_FINE_LOCATION) == PERMISSION_GRANTED
        Granularity.GRANULARITY_COARSE -> ctx.checkSelfPermission(ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED
        else -> true
    }

Try / catch

try {
    client.requestLocationUpdates(request, listener, looper)
} catch (e: RuntimeException) {
    if (e.message == "Lack of permission") {
        downgradeToCoarseRequest(); promptForPermission()
    }
}

Prevention

When it happens

Trigger: Registering a location request (holder construction after changedGranularity) when checkAppOpForEffectiveGranularity returns false — e.g. the app has ACCESS_FINE_LOCATION declared but the system app-op for fine location is denied, or the request granularity exceeds what the app-op allows.

Common situations: User revoked location via AppOps (adb shell appops set <pkg> FINE_LOCATION deny) while the app keeps requesting; ROMs where runtime grant and app-op state diverge; microG self-check mismatch after permission upgrades.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.

Related errors


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