microg/GmsCore · error · IllegalArgumentException

key data missing

Error message

key data missing

What it means

TemporaryExposureKeyProto.toKey() converts a protobuf-encoded exposure key into the API TemporaryExposureKey object. The key_data field is mandatory; when the proto was received without key data, the elvis operator throws IllegalArgumentException 'key data missing' instead of building a key with null/empty key material.

Source

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

                else -> emptyList()
            }

            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)

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Validate each proto entry's key_data is non-null/32 bytes before calling toKey
  2. Re-download the exposure key export file and verify its signature/checksum
  3. Skip or log entries with missing key_data instead of propagating the exception
  4. Verify the export was fully downloaded (file size / completion) before parsing

Example fix

// before
private fun TemporaryExposureKeyProto.toKey() = Builder().setKeyData(key_data?.toByteArray() ?: throw IllegalArgumentException("key data missing"))...
// after
fun TemporaryExposureKeyProto.toKeyOrNull(): TemporaryExposureKey? {
    val data = key_data?.toByteArray() ?: return null
    return TemporaryExposureKey.TemporaryExposureKeyBuilder().setKeyData(data)
        .setRollingStartIntervalNumber(rolling_start_interval_number ?: return null)
        .setRollingPeriod(rolling_period ?: return null)
        .setTransmissionRiskLevel(transmission_risk_level ?: 0).build()
}
Defensive patterns

Strategy: validation

Validate before calling

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

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()
    use(key)
} catch (e: IllegalArgumentException) {
    Log.w(TAG, "Skipping malformed exposure key: ${e.message}")
}

Prevention

When it happens

Trigger: A key file (ZIP of exported exposure keys) contains a TemporaryExposureKey proto entry with key_data unset or empty; a malformed export is downloaded and parsed, then toKey() is invoked on each entry.

Common situations: Corrupt or truncated exposure-key export files; keys downloaded over a flaky connection and partially written; manually constructed or test protos missing key_data; mismatched export format versions.

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/c01f895189584589. Report an issue: GitHub.