microg/GmsCore · error · RuntimeException
max updates reached
Error message
max updates reached
What it means
microG's LocationRequestManager tracks how many location updates a client request is still entitled to (updatesPending, derived from the request's update budget). Each incoming location is validated by check() before delivery. When the request's update count is exhausted (updatesPending <= 0), delivering further updates would violate the client's LocationRequest contract, so a RuntimeException 'max updates reached' is thrown.
Source
Thrown at play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt:458
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() {
if (!context.checkAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) throw RuntimeException("Lack of permission")
if (effectiveGranularity > permissionGranularity) throw RuntimeException("Lack of permission")
if (timePendingMillis < 0) throw RuntimeException("duration limit reached (active for ${(SystemClock.elapsedRealtime() - start).formatDuration()}, duration ${request.durationMillis.formatDuration()})")
if (updatesPending <= 0) throw RuntimeException("max updates reached")
if (callback?.asBinder()?.isBinderAlive == false) throw RuntimeException("Binder died")
}
fun processNewLocation(location: Location): Boolean {
check()
if (lastLocation != null && location.elapsedMillis - lastLocation!!.elapsedMillis < request.minUpdateIntervalMillis) return false
if (lastLocation != null && location.distanceTo(lastLocation!!) < request.minUpdateDistanceMeters) return false
if (lastLocation == location) return false
val returnedLocation = if (effectiveGranularity > permissionGranularity) {
throw RuntimeException("Lack of permission")
} else {
if (!context.noteAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) {
throw RuntimeException("app op denied")
} else if (clientIdentity.isSelfProcess()) {
Location(location)
} else {
Location(location).apply { provider = "fused" }
}View on GitHub (pinned to 157c9d86ac)
Solutions
- Verify the LocationRequest's numUpdates is set high enough (or left unlimited) for the app's use case
- Call setNumUpdates(Integer.MAX_VALUE) if continuous updates are intended instead of a fixed count
- Re-register a fresh LocationRequest via FusedLocationProviderClient.requestLocationUpdates once the old one's budget is spent
- Check for duplicate request registrations that drain the shared update budget faster than expected
- Remove the listener with removeLocationUpdates when done so exhausted requests are not re-invoked
Example fix
// before
LocationRequest.Builder(builder)
.setNumUpdates(1)
.build() // throws on the 2nd incoming location
// after
LocationRequest.Builder(builder)
.setNumUpdates(10) // or Integer.MAX_VALUE for continuous
.build() Defensive patterns
Strategy: try-catch
Validate before calling
// compute remaining updates before registering
val updates = request.numUpdates
if (updates <= 0) throw IllegalArgumentException("LocationRequest already exhausted; create a new request") Type guard
fun LocationRequest.hasUpdateBudget(): Boolean = numUpdates > 0 || durationPending
Try / catch
try {
client.requestLocationUpdates(request, listener, looper)
} catch (e: RuntimeException) {
if (e.message == "max updates reached") {
client.requestLocationUpdates(request.toUnboundedBuilder().build(), listener, looper)
} else throw e
} Prevention
- Set numUpdates explicitly and high enough (Integer.MAX_VALUE for continuous updates)
- Treat numUpdates as cumulative over the request lifetime, not per session
- Recreate the LocationRequest when resuming instead of reusing an expired one
- Remove listeners when finished to avoid draining shared budgets
When it happens
Trigger: A client registered a LocationRequest with a bounded number of updates (e.g. setNumUpdates(n) or a duration-limited request) and all n updates have already been delivered; a new location arrives and processNewLocation() calls check() which sees updatesPending <= 0.
Common situations: Apps that forgot the update count is cumulative across the request lifetime; reusing a stale LocationRequest after its budget ran out; requesting numUpdates=1 expecting continuous updates; duration-limited foreground-only requests kept alive after expiry.
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
- Access denied, missing google package permission for
- Required caller information missing
- ERROR_MISSING_INSTANCEID_SERVICE
- invalid radius:
- invalid latitude:
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/966d1e5b23600987.
Report an issue: GitHub.