{"record":{"id":"af2ea853eaf851d4","repo":"grpc/grpc-java","slug":"corrupt-gzip-trailer","errorCode":null,"errorMessage":"Corrupt GZIP trailer","messagePattern":"Corrupt GZIP trailer","errorType":"exception","errorClass":"ZipException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/io/grpc/internal/GzipInflatingBuffer.java","lineNumber":468,"sourceCode":"    gzippedData.readBytes(inflaterInput, inflaterInputStart, bytesToAdd);\n    inflater.setInput(inflaterInput, inflaterInputStart, bytesToAdd);\n    state = State.INFLATING;\n    return true;\n  }\n\n  private boolean processTrailer() throws ZipException {\n    if (inflater != null\n        && gzipMetadataReader.readableBytes() <= GZIP_HEADER_MIN_SIZE + GZIP_TRAILER_SIZE) {\n      // We don't have enough bytes to begin inflating a concatenated gzip stream, drop context\n      inflater.end();\n      inflater = null;\n    }\n    if (gzipMetadataReader.readableBytes() < GZIP_TRAILER_SIZE) {\n      return false;\n    }\n    if (crc.getValue() != gzipMetadataReader.readUnsignedInt()\n        || expectedGzipTrailerIsize != gzipMetadataReader.readUnsignedInt()) {\n      throw new ZipException(\"Corrupt GZIP trailer\");\n    }\n    crc.reset();\n    state = State.HEADER;\n    return true;\n  }\n}\n","sourceCodeStart":450,"sourceCodeEnd":475,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/core/src/main/java/io/grpc/internal/GzipInflatingBuffer.java#L450-L475","documentation":"Each gzip member ends with an 8-byte trailer: CRC32 of the uncompressed data and ISIZE (uncompressed size mod 2^32). processTrailer compares both against the values tracked while inflating; any mismatch throws ZipException('Corrupt GZIP trailer'), meaning the decompressed bytes do not match what the compressor committed to.","triggerScenarios":"After finishing a gzip member, the CRC32 of all inflated output differs from the trailer's CRC field, or the byte count differs from ISIZE — bit corruption, truncation, or body rewriting anywhere between compression and decompression.","commonSituations":"Flaky network or NIC corruption; proxies/caches that re-encode bodies and forget to recompute the trailer; storage corruption of cached responses; concurrent writes to the compressed blob.","solutions":["Run `gzip -t` on a captured payload to confirm trailer corruption occurs upstream","Bypass or fix body-rewriting intermediaries (recompressing proxies, CDN caches)","Retry the RPC — single-bit corruption is often transient; persistent failure points at the sender","Add TLS if the traffic path is unencrypted, since TLS integrity checks catch such corruption"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// pre-validate payload with an independent gzip decoder:\ntry (GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(payload))) { in.readAllBytes(); }","typeGuard":null,"tryCatchPattern":"try { call(...); } catch (StatusRuntimeException e) {\n  if (e.getCause() instanceof ZipException && e.getCause().getMessage().contains(\"Corrupt GZIP trailer\")) {\n    // discard response and retry once; escalate if it recurs\n  } else throw e;\n}","preventionTips":["Enable TLS to detect corruption in transit","Avoid caches/proxies that re-encode bodies without recomputing gzip trailers","Retry once on transient corruption; persistent failures indicate sender-side breakage"],"tags":["grpc","gzip","checksum","trailer","data-corruption"],"backgroundTag":"checksum-mismatch","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}