{"record":{"id":"f60dce04f4d11713","repo":"microg/GmsCore","slug":"rolling-period-missing","errorCode":null,"errorMessage":"rolling period missing","messagePattern":"rolling period missing","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"play-services-nearby/core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt","lineNumber":250,"sourceCode":"            ExposureDatabase.with(context) { database ->\n                database.noteAppAction(packageName, \"getTemporaryExposureKeyHistory\", JSONObject().apply {\n                    put(\"result\", status.statusCode)\n                    put(\"response_keys_size\", response.size)\n                }.toString())\n            }\n            try {\n                params.callback.onResult(status, response)\n            } catch (e: Exception) {\n                Log.w(TAG, \"Callback failed\", e)\n            }\n        }\n    }\n\n    private fun TemporaryExposureKeyProto.toKey(): TemporaryExposureKey = TemporaryExposureKey.TemporaryExposureKeyBuilder()\n            .setKeyData(key_data?.toByteArray() ?: throw IllegalArgumentException(\"key data missing\"))\n            .setRollingStartIntervalNumber(rolling_start_interval_number\n                    ?: throw IllegalArgumentException(\"rolling start interval number missing\"))\n            .setRollingPeriod(rolling_period ?: throw IllegalArgumentException(\"rolling period missing\"))\n            .setTransmissionRiskLevel(transmission_risk_level ?: 0)\n            .build()\n\n    private fun InputStream.copyToFile(outputFile: File) {\n        outputFile.outputStream().use { output ->\n            copyTo(output)\n            output.flush()\n        }\n    }\n\n    private fun MessageDigest.digest(file: File): ByteArray = file.inputStream().use { input ->\n        val buf = ByteArray(4096)\n        var bytes = input.read(buf)\n        while (bytes != -1) {\n            update(buf, 0, bytes)\n            bytes = input.read(buf)\n        }\n        digest()","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-nearby/core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt#L232-L268","documentation":"TemporaryExposureKeyProto.toKey() requires rolling_period (key validity length in 10-minute intervals). If the received proto omits it, IllegalArgumentException 'rolling period missing' is thrown, since risk calculation and key matching depend on this field.","triggerScenarios":"Converting a TemporaryExposureKeyProto from an export file or server response where rolling_period is unset; malformed diagnosis keys fetched by provideDiagnosisKeys.","commonSituations":"Noncompliant diagnosis servers omitting rolling_period; truncated/corrupt key archives; older export formats or hand-built test protos missing the field.","solutions":["Validate rolling_period is present and within the expected range (e.g. 1-144) before conversion","Re-fetch the export file and verify integrity (signature/checksum)","Skip entries with missing rolling_period and log a warning","Fix the upstream diagnosis key server to emit complete TEK protos"],"exampleFix":"// before\n.setRollingPeriod(rolling_period ?: throw IllegalArgumentException(\"rolling period missing\"))\n// after\nval period = rolling_period?.takeIf { it in 1..144 } ?: run { Log.w(TAG, \"invalid rolling period\"); return null }\n.setRollingPeriod(period)","handlingStrategy":"validation","validationCode":"fun TemporaryExposureKeyProto.hasValidRollingPeriod(): Boolean =\n    rolling_period != null && rolling_period in 1..144","typeGuard":"fun TemporaryExposureKeyProto?.isConvertibleToKey(): Boolean =\n    this != null && key_data?.size() ?: 0 > 0 && rolling_start_interval_number != null && rolling_period != null","tryCatchPattern":"try {\n    val key = proto.toKey()\n} catch (e: IllegalArgumentException) {\n    if (e.message?.contains(\"rolling period\") == true) skipEntry(proto)\n    else throw e\n}","preventionTips":["Check rolling_period presence and range before conversion","Verify export file integrity before parsing","Skip and log entries with invalid rolling periods","Fix upstream key servers to always emit rolling_period"],"tags":["nearby","exposure-notification","protobuf","validation"],"backgroundTag":"empty-required-field","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"}