{"record":{"id":"4cbf502fcb49a94b","repo":"microg/GmsCore","slug":"max-updates-reached","errorCode":null,"errorMessage":"max updates reached","messagePattern":"max updates reached","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/DeviceOrientationManager.kt","lineNumber":341,"sourceCode":"            private val request: DeviceOrientationRequest,\n            private val listener: IDeviceOrientationListener,\n        ) {\n            private var updates = 0\n            private var lastOrientation: DeviceOrientation? = null\n\n            val updatesPending: Int\n                get() = request.numUpdates - updates\n            val timePendingMillis: Long\n                get() = request.expirationTime - SystemClock.elapsedRealtime()\n            val workSource = WorkSource().also { WorkSourceUtil.add(it, clientIdentity.uid, clientIdentity.packageName) }\n\n            fun processNewDeviceOrientation(deviceOrientation: DeviceOrientation) {\n                if (timePendingMillis < 0) throw RuntimeException(\"duration limit reached (expired at ${request.expirationTime}, now is ${SystemClock.elapsedRealtime()})\")\n                if (lastOrientation != null && abs(lastOrientation!!.headingDegrees - deviceOrientation.headingDegrees) < Math.toDegrees(request.smallestAngleChangeRadians.toDouble())) return\n                if (lastOrientation == deviceOrientation) return\n                listener.onDeviceOrientationChanged(deviceOrientation)\n                if (request.numUpdates != Int.MAX_VALUE) updates++\n                if (updatesPending <= 0) throw RuntimeException(\"max updates reached\")\n            }\n        }\n    }\n}","sourceCodeStart":323,"sourceCodeEnd":345,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/DeviceOrientationManager.kt#L323-L345","documentation":"DeviceOrientationManager's processNewDeviceOrientation throws RuntimeException(\"max updates reached\") once the number of delivered updates has consumed request.numUpdates (updatesPending <= 0). This enforces the one-shot/limited-update semantics of the orientation request.","triggerScenarios":"A DeviceOrientationRequest specifying a finite numUpdates receives orientation changes beyond that count — the client keeps receiving events after the update quota is exhausted.","commonSituations":"Requests created with small numUpdates (e.g. 1) while the sensor keeps reporting; callers not unregistering after receiving all updates; duplicated sensor registrations each incrementing the shared counter.","solutions":["Unregister the listener after receiving numUpdates callbacks","Set numUpdates = Int.MAX_VALUE if continuous updates are desired","Catch the RuntimeException and clean up/re-register as needed","Ensure only one request per client increments the update counter (avoid duplicate registrations)"],"exampleFix":"// before\nmanager.addDeviceOrientationListener(request, executor, callback) // numUpdates = 1, never removed\n// after\nval callback = object : DeviceOrientationCallback {\n    override fun onDeviceOrientationChanged(o: DeviceOrientation) {\n        handle(o)\n        if (++received >= request.numUpdates) manager.removeDeviceOrientationListener(this)\n    }\n}","handlingStrategy":"try-catch","validationCode":"if (receivedUpdates >= request.numUpdates) {\n    manager.removeDeviceOrientationListener(listener)\n}","typeGuard":"fun DeviceOrientationRequest.isUpdateQuotaExhausted(received: Int): Boolean =\n    numUpdates != Int.MAX_VALUE && received >= numUpdates","tryCatchPattern":"try {\n    handleOrientation(orientation)\n} catch (e: RuntimeException) {\n    if (e.message == \"max updates reached\") unregisterAndCleanup()\n    else throw e\n}","preventionTips":["Unregister the listener after numUpdates callbacks arrive","Use numUpdates = Int.MAX_VALUE for continuous tracking","Avoid duplicate registrations incrementing the same counter","Treat the exception as an end-of-session signal, not a failure"],"tags":["android","location","device-orientation","update-limit"],"backgroundTag":"invalid-state-transition","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}