microg/GmsCore · error · StandardIntegrityException

IntegrityErrorCode.NONCE_TOO_LONG

IntegrityErrorCode.NONCE_TOO_LONG

Error message

Nonce too long.

What it means

The nonce byte array supplied to requestIntegrityToken is 500 bytes or larger, exceeding the service's maximum. The nonce is embedded in the signed response, so oversized values are rejected with IntegrityErrorCode.NONCE_TOO_LONG.

Source

Thrown at vending-app/src/main/kotlin/com/google/android/finsky/integrityservice/IntegrityService.kt:125

                    throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, "Null packageName.")
                }
                integrityData = callerAppToIntegrityData(context, packageName)
                if (integrityData?.allowed != true) {
                    throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, "Not allowed to request integrity token.")
                }
                val playIntegrityEnabled = VendingPreferences.isDeviceAttestationEnabled(context)
                if (!playIntegrityEnabled) {
                    throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, "API is disabled.")
                }
                val nonceArr = request.getByteArray(KEY_NONCE)
                if (nonceArr == null) {
                    throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, "Nonce missing.")
                }
                if (nonceArr.size < 16) {
                    throw StandardIntegrityException(IntegrityErrorCode.NONCE_TOO_SHORT, "Nonce too short.")
                }
                if (nonceArr.size >= 500) {
                    throw StandardIntegrityException(IntegrityErrorCode.NONCE_TOO_LONG, "Nonce too long.")
                }
                val cloudProjectNumber = request.getLong(KEY_CLOUD_PROJECT, 0L)
                val playCoreVersion = request.getPlayCoreVersion()
                Log.d(TAG, "requestIntegrityToken(packageName: $packageName, nonce: ${nonceArr.encodeBase64(false)}, cloudProjectNumber: $cloudProjectNumber, playCoreVersion: $playCoreVersion)")

                val packageInfo = context.packageManager.getPackageInfoCompat(packageName, SIGNING_FLAGS)
                val timestamp = makeTimestamp(System.currentTimeMillis())
                val versionCode = packageInfo.versionCode

                val integrityParams = IntegrityParams(
                    packageName = PackageNameWrapper(packageName),
                    versionCode = VersionCodeWrapper(versionCode),
                    nonce = nonceArr.encodeBase64(noPadding = false, noWrap = true, urlSafe = true),
                    certificateSha256Digests = packageInfo.signaturesCompat.map {
                        it.toByteArray().sha256().encodeBase64(noPadding = true, noWrap = true, urlSafe = true)
                    },
                    timestampAtRequest = timestamp,
                    cloudProjectNumber = cloudProjectNumber.takeIf { it > 0L }

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Hash the large payload and use the digest as the nonce: MessageDigest.getInstance("SHA-256").digest(payload) (32 bytes), storing the full payload server-side for comparison.
  2. Check nonceArr.size < 500 at the call site before sending the request.
  3. Remove unnecessary fields from the nonce payload; keep it to a compact, hashed summary.
  4. Avoid redundant encodings (e.g. Base64-encoding an already-encoded string) that inflate byte size.

Example fix

// before
val nonce = buildJsonString(requestDetails).toByteArray() // can exceed 500 bytes
// after
val nonce = MessageDigest.getInstance("SHA-256")
    .digest(buildJsonString(requestDetails).toByteArray()) // 32 bytes
serverNonceStore.store(expectedHash = nonce)
Defensive patterns

Strategy: validation

Validate before calling

fun isNonceTooLong(nonce: ByteArray) = nonce.size >= 500
if (isNonceTooLong(nonce)) nonce = MessageDigest.getInstance("SHA-256").digest(nonce)

Type guard

fun ByteArray?.isNonceValid(): Boolean = this != null && size in 16..499

Try / catch

try {
    integrityManager.requestIntegrityToken(request)
} catch (e: StandardIntegrityException) {
    if (e.errorCode == IntegrityErrorCode.NONCE_TOO_LONG) {
        hashPayloadAndRetry()
    } else throw e
}

Prevention

When it happens

Trigger: Calling requestIntegrityToken with a KEY_NONCE byte array whose size >= 500, e.g. serializing an entire request/response body, large JSON blob, or concatenated data into the nonce.

Common situations: Putting a full JSON document of request details into the nonce instead of a hash; appending debug/trace data; double-encoding (Base64 of Base64) inflating size; passing a whole file's bytes.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


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