microg/GmsCore · error · RuntimeException
duration limit reached (expired at ${request.expirationTime}
Error message
duration limit reached (expired at ${request.expirationTime}, now is ${SystemClock.elapsedRealtime()}) What it means
DeviceOrientationManager's per-request callback processNewDeviceOrientation throws RuntimeException("duration limit reached ...") when a new orientation event arrives after request.expirationTime has passed (timePendingMillis < 0). The duration-limited update session has expired and must not deliver further callbacks.
Source
Thrown at play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/DeviceOrientationManager.kt:336
const val SAMPLING_PERIOD_US = 20_000
const val MAX_REPORT_LATENCY_US = 200_000
private class DeviceOrientationRequestHolder(
val clientIdentity: ClientIdentity,
private val request: DeviceOrientationRequest,
private val listener: IDeviceOrientationListener,
) {
private var updates = 0
private var lastOrientation: DeviceOrientation? = null
val updatesPending: Int
get() = request.numUpdates - updates
val timePendingMillis: Long
get() = request.expirationTime - SystemClock.elapsedRealtime()
val workSource = WorkSource().also { WorkSourceUtil.add(it, clientIdentity.uid, clientIdentity.packageName) }
fun processNewDeviceOrientation(deviceOrientation: DeviceOrientation) {
if (timePendingMillis < 0) throw RuntimeException("duration limit reached (expired at ${request.expirationTime}, now is ${SystemClock.elapsedRealtime()})")
if (lastOrientation != null && abs(lastOrientation!!.headingDegrees - deviceOrientation.headingDegrees) < Math.toDegrees(request.smallestAngleChangeRadians.toDouble())) return
if (lastOrientation == deviceOrientation) return
listener.onDeviceOrientationChanged(deviceOrientation)
if (request.numUpdates != Int.MAX_VALUE) updates++
if (updatesPending <= 0) throw RuntimeException("max updates reached")
}
}
}
}View on GitHub (pinned to 157c9d86ac)
Solutions
- Listen for the expiration and request a new DeviceOrientationRequest before it expires
- Set expirationTime/numUpdates generously (or Int.MAX_VALUE updates, long duration) for continuous use
- Catch the RuntimeException around the update callback and re-register the request
- Verify elapsedRealtime vs wall-clock assumptions if events arrive at unexpected times
Example fix
// before
listener = manager.addDeviceOrientationListener(request, executor, listener)
// keep listening forever...
// after
scope.launch {
delay(request.expirationTime - SystemClock.elapsedRealtime())
manager.removeDeviceOrientationListener(listener)
registerNewRequest() // fresh expiration
} Defensive patterns
Strategy: try-catch
Validate before calling
if (SystemClock.elapsedRealtime() >= expirationTime) {
reRegisterRequest() // skip starting/stopping on an expired request
} Type guard
fun DeviceOrientationRequest.isExpired(): Boolean =
expirationTime - SystemClock.elapsedRealtime() <= 0 Try / catch
try {
handleOrientation(orientation)
} catch (e: RuntimeException) {
if (e.message?.startsWith("duration limit reached") == true) reRegisterRequest()
else throw e
} Prevention
- Re-register the request before expirationTime elapses
- Track expiration in the client and stop listening proactively
- Use long durations for continuous monitoring needs
- Account for deep sleep when computing elapsed budgets
When it happens
Trigger: A DeviceOrientationRequest with an expirationTime (durationMs) receives an orientation event after that elapsed-realtime deadline — the client kept listening without refreshing the request.
Common situations: Long-lived orientation listeners with a fixed duration request; system clock/elapsedRealtime anomalies after deep sleep; failing to re-register a new request after the previous one expires.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/644bb5bd5b01fafb.
Report an issue: GitHub.