golang/go · error
zlib.ErrChecksum
zlib.ErrChecksum
Error message
zlib: invalid checksum
What it means
Returned by zlib.Reader.Read when the Adler-32 checksum stored in the zlib trailer does not match the Adler-32 of the bytes the deflate decompressor actually produced. Computed via hash/adler32 incrementally, compared once EOF is reached.
Source
Thrown at src/compress/zlib/reader.go:43
import (
"bufio"
"compress/flate"
"encoding/binary"
"errors"
"hash"
"hash/adler32"
"io"
)
const (
zlibDeflate = 8
zlibMaxWindow = 7
)
var (
// ErrChecksum is returned when reading ZLIB data that has an invalid checksum.
ErrChecksum = errors.New("zlib: invalid checksum")
// ErrDictionary is returned when reading ZLIB data that has an invalid dictionary.
ErrDictionary = errors.New("zlib: invalid dictionary")
// ErrHeader is returned when reading ZLIB data that has an invalid header.
ErrHeader = errors.New("zlib: invalid header")
)
type reader struct {
r flate.Reader
decompressor io.ReadCloser
digest hash.Hash32
err error
scratch [4]byte
}
// Resetter resets a ReadCloser returned by [NewReader] or [NewReaderDict]
// to switch to a new underlying Reader. This permits reusing a ReadCloser
// instead of allocating a new one.
type Resetter interface {View on GitHub (pinned to b6b368adc5)
Solutions
- Re-fetch the source payload from the origin; truncation is the most common cause.
- If you control the producer, ensure deflater.Close() is called so the Adler-32 trailer is written.
- Verify with Python's zlib or `openssl zlib` (where available) to confirm the corruption is in the data, not in your reader wiring.
- Wrap the transport in TLS so corruption surfaces as a MAC failure with clearer diagnostics.
Example fix
// before
if _, err := io.Copy(out, zr); err != nil { return err } // opaque
// after: distinguish checksum failures
if _, err := io.Copy(out, zr); err != nil {
if errors.Is(err, zlib.ErrChecksum) {
return fmt.Errorf("payload corrupt (adler32 mismatch): %w", err)
}
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
import "hash/adler32"
// Full pre-validation requires decompressing. Partial checks:
func likelyZlib(data []byte) error {
if len(data) < 2 { return errors.New("too short") }
cmf, flg := data[0], data[1]
if cmf&0x0f != 8 { return zlib.ErrHeader }
if (uint16(cmf)<<8 | uint16(flg)) % 31 != 0 { return zlib.ErrHeader }
return nil
}
// Note: Adler-32 can only be fully validated after decompression. Type guard
func isZlibChecksumErr(err error) bool {
return errors.Is(err, zlib.ErrChecksum)
} Try / catch
if _, err := io.Copy(out, zr); err != nil {
if errors.Is(err, zlib.ErrChecksum) {
return corruptStreamError{kind: "zlib-adler32", cause: err}
}
return err
} Prevention
- Always Close the writer so the Adler-32 trailer is emitted.
- Transport zlib over integrity-protected channels (TLS).
- For HTTP, prefer gzip over raw deflate/zlib — wider tooling support.
When it happens
Trigger: Reading from a zlib.NewReader where the trailing 4 bytes (Adler-32, big-endian) disagree with the decompressed payload. Surfaced at the moment the trailer is parsed, i.e., after the last deflate byte has been consumed.
Common situations: Truncated HTTP body framed with Content-Encoding: zlib, decompression of a payload that passed through a buggy proxy that re-chunked but did not recompute Adler-32, network byte corruption, or a producer that forgot to flush Close so the trailer is missing.
Related errors
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/a826152b19a8d3d9.
Report an issue: GitHub.