grpc/grpc-java · error · ZipException

Corrupt GZIP header

Error message

Corrupt GZIP header

What it means

RFC 1952 optionally ends the gzip header with a CRC16 of the header bytes plus the FLG byte. processHeaderCrc compares the stored CRC16 against one it computes while reading the header. A mismatch means the header bytes were altered between write and read, so a ZipException('Corrupt GZIP header') is thrown.

Source

Thrown at core/src/main/java/io/grpc/internal/GzipInflatingBuffer.java:390

    }
    if (!gzipMetadataReader.readBytesUntilZero()) {
      return false;
    }
    state = State.HEADER_CRC;
    return true;
  }

  private boolean processHeaderCrc() throws ZipException {
    if ((gzipHeaderFlag & HEADER_CRC_FLAG) != HEADER_CRC_FLAG) {
      state = State.INITIALIZE_INFLATER;
      return true;
    }
    if (gzipMetadataReader.readableBytes() < UNSIGNED_SHORT_SIZE) {
      return false;
    }
    int desiredCrc16 = (int) crc.getValue() & 0xffff;
    if (desiredCrc16 != gzipMetadataReader.readUnsignedShort()) {
      throw new ZipException("Corrupt GZIP header");
    }
    state = State.INITIALIZE_INFLATER;
    return true;
  }

  private boolean initializeInflater() {
    if (inflater == null) {
      inflater = new Inflater(true);
    } else {
      inflater.reset();
    }
    crc.reset();
    int bytesRemainingInInflaterInput = inflaterInputEnd - inflaterInputStart;
    if (bytesRemainingInInflaterInput > 0) {
      inflater.setInput(inflaterInput, inflaterInputStart, bytesRemainingInInflaterInput);
      state = State.INFLATING;
    } else {
      state = State.INFLATER_NEEDS_INPUT;

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Capture the raw bytes and run `gzip -t` to confirm corruption is upstream of the client
  2. Disable intermediaries that rewrite the body (compression-aware proxies, WAFs) or mark the stream unbufferable
  3. Regenerate the payload with a standard gzip implementation (GZIPOutputStream / `gzip`) instead of hand-rolled header construction
  4. Retry the request over a clean connection in case of transient corruption
Defensive patterns

Strategy: retry

Validate before calling

// CRC16 check before consuming the stream is impractical client-side; verify offline:
// gzip -t payload.gz && echo OK

Try / catch

try { call(...); } catch (StatusRuntimeException e) {
  if (e.getCause() instanceof ZipException && e.getCause().getMessage().contains("Corrupt GZIP header")) {
    return retryWithFreshConnection(request); // transient corruption
  } throw e;
}

Prevention

When it happens

Trigger: A gzip member whose header sets the FHCBC flag (header checksum present) but whose stored CRC16 does not match the CRC computed over the bytes actually received — corruption, truncation, or rewriting of the header.

Common situations: Middleboxes or service meshes rewriting headers (e.g. injecting bytes); corrupted cache entries; partial reads from an interrupted stream; hand-crafted gzip payloads built with a wrong CRC.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/ddeebf4dde8485a9. Report an issue: GitHub.