grpc/grpc-java · error · ZipException

Not in GZIP format

Error message

Not in GZIP format

What it means

GzipInflatingBuffer decompresses gZIP-framed data and must first read the GZIP header, which starts with the magic bytes 0x1f8b. If the stream's first two bytes do not match GZIP_MAGIC, it throws java.util.zip.ZipException("Not in GZIP format"), meaning the payload is not gzip-compressed data despite being announced as such.

Source

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

          break;
        default:
          throw new AssertionError("Invalid state: " + state);
      }
    }
    // If we finished a gzip block, check if we have enough bytes to read another header
    isStalled =
        !madeProgress
            || (state == State.HEADER && gzipMetadataReader.readableBytes() < GZIP_HEADER_MIN_SIZE);

    return bytesRead;
  }

  private boolean processHeader() throws ZipException {
    if (gzipMetadataReader.readableBytes() < GZIP_HEADER_MIN_SIZE) {
      return false;
    }
    if (gzipMetadataReader.readUnsignedShort() != GZIP_MAGIC) {
      throw new ZipException("Not in GZIP format");
    }
    if (gzipMetadataReader.readUnsignedByte() != 8) {
      throw new ZipException("Unsupported compression method");
    }
    gzipHeaderFlag = gzipMetadataReader.readUnsignedByte();
    gzipMetadataReader.skipBytes(6 /* remaining header bytes */);
    state = State.HEADER_EXTRA_LEN;
    return true;
  }

  private boolean processHeaderExtraLen() {
    if ((gzipHeaderFlag & HEADER_EXTRA_FLAG) != HEADER_EXTRA_FLAG) {
      state = State.HEADER_NAME;
      return true;
    }
    if (gzipMetadataReader.readableBytes() < UNSIGNED_SHORT_SIZE) {
      return false;
    }

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Verify both peers agree on the compression encoding (grpc-encoding: gzip) and only send gzip when negotiated; otherwise send identity.
  2. Check for truncation/corruption (proxy buffering, content-length mismatch) that chops the gzip header bytes.
  3. Remove double-compression: if an interceptor already gzips payloads, don't also enable transport gzip.
  4. Catch ZipException on the decompression path and fall back to identity decoding when the magic bytes are missing.

Example fix

// before
byte[] payload = plainBytes; // advertised as gzip
// after
byte[] payload = gzip(plainBytes); // actually compress before sending with grpc-encoding: gzip
Defensive patterns

Strategy: try-catch

Validate before calling

if (data.length < 2 || (data[0] & 0xff) != 0x1f || (data[1] & 0xff) != 0x8b) {
  throw new IllegalArgumentException("payload is not gzip; grpc-encoding mismatch?");
}

Type guard

boolean isGzip(byte[] b) { return b != null && b.length >= 2 && b[0] == 0x1f && b[1] == (byte) 0x8b; }

Try / catch

try {
  msg = gzipDecompressor.decompress(frame);
} catch (ZipException e) {
  // treat as identity or fail the call with INTERNAL status
}

Prevention

When it happens

Trigger: A message declared with the 'gzip' gRPC encoding is actually plain (identity) bytes, truncated, or double-wrapped in another container, so inflateBytes/processHeader reads a non-0x1f8b magic number at the start of the stream.

Common situations: Server/client encoding negotiation mismatch (content-coding says gzip but data is raw); corrupt or truncated response bodies; misconfigured compression interceptors or proxies; double-decompression where an outer layer already inflated the data.

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