microg/GmsCore · error · IllegalArgumentException

rolling period missing

Error message

rolling period missing

What it means

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.

Source

Thrown at play-services-nearby/core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt:250

            ExposureDatabase.with(context) { database ->
                database.noteAppAction(packageName, "getTemporaryExposureKeyHistory", JSONObject().apply {
                    put("result", status.statusCode)
                    put("response_keys_size", response.size)
                }.toString())
            }
            try {
                params.callback.onResult(status, response)
            } catch (e: Exception) {
                Log.w(TAG, "Callback failed", e)
            }
        }
    }

    private fun TemporaryExposureKeyProto.toKey(): TemporaryExposureKey = TemporaryExposureKey.TemporaryExposureKeyBuilder()
            .setKeyData(key_data?.toByteArray() ?: throw IllegalArgumentException("key data missing"))
            .setRollingStartIntervalNumber(rolling_start_interval_number
                    ?: throw IllegalArgumentException("rolling start interval number missing"))
            .setRollingPeriod(rolling_period ?: throw IllegalArgumentException("rolling period missing"))
            .setTransmissionRiskLevel(transmission_risk_level ?: 0)
            .build()

    private fun InputStream.copyToFile(outputFile: File) {
        outputFile.outputStream().use { output ->
            copyTo(output)
            output.flush()
        }
    }

    private fun MessageDigest.digest(file: File): ByteArray = file.inputStream().use { input ->
        val buf = ByteArray(4096)
        var bytes = input.read(buf)
        while (bytes != -1) {
            update(buf, 0, bytes)
            bytes = input.read(buf)
        }
        digest()

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Validate rolling_period is present and within the expected range (e.g. 1-144) before conversion
  2. Re-fetch the export file and verify integrity (signature/checksum)
  3. Skip entries with missing rolling_period and log a warning
  4. Fix the upstream diagnosis key server to emit complete TEK protos

Example fix

// before
.setRollingPeriod(rolling_period ?: throw IllegalArgumentException("rolling period missing"))
// after
val period = rolling_period?.takeIf { it in 1..144 } ?: run { Log.w(TAG, "invalid rolling period"); return null }
.setRollingPeriod(period)
Defensive patterns

Strategy: validation

Validate before calling

fun TemporaryExposureKeyProto.hasValidRollingPeriod(): Boolean =
    rolling_period != null && rolling_period in 1..144

Type guard

fun TemporaryExposureKeyProto?.isConvertibleToKey(): Boolean =
    this != null && key_data?.size() ?: 0 > 0 && rolling_start_interval_number != null && rolling_period != null

Try / catch

try {
    val key = proto.toKey()
} catch (e: IllegalArgumentException) {
    if (e.message?.contains("rolling period") == true) skipEntry(proto)
    else throw e
}

Prevention

When it happens

Trigger: Converting a TemporaryExposureKeyProto from an export file or server response where rolling_period is unset; malformed diagnosis keys fetched by provideDiagnosisKeys.

Common situations: Noncompliant diagnosis servers omitting rolling_period; truncated/corrupt key archives; older export formats or hand-built test protos missing the field.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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