hashicorp/nomad · error
failed to decompress snapshot: %v
Error message
failed to decompress snapshot: %v
What it means
CopySnapshot wraps gzip.NewReader failures when opening the incoming snapshot stream for decompression. The stream is not valid gzip data from the very first bytes, so the metadata cannot be read and the copy aborts. Raised when callers stream snapshots in (e.g. from an HTTP restore endpoint) and Verify validates them.
Source
Thrown at helper/snapshot/snapshot.go:205
io.Writer
}
func (dc Discard) Close() error { return nil }
// Verify takes the snapshot from the reader and verifies its contents.
func Verify(in io.Reader) (*raft.SnapshotMeta, error) {
return CopySnapshot(in, Discard{Writer: io.Discard})
}
// CopySnapshot copies the snapshot content from snapshot archive to dest.
// It will close the destination once complete.
func CopySnapshot(in io.Reader, dest io.WriteCloser) (*raft.SnapshotMeta, error) {
defer dest.Close()
// Wrap the reader in a gzip decompressor.
decomp, err := gzip.NewReader(in)
if err != nil {
return nil, fmt.Errorf("failed to decompress snapshot: %v", err)
}
defer decomp.Close()
// Read the archive, throwing away the snapshot data.
var metadata raft.SnapshotMeta
if err := read(decomp, &metadata, dest); err != nil {
return nil, fmt.Errorf("failed to read snapshot file: %v", err)
}
if err := concludeGzipRead(decomp); err != nil {
return nil, err
}
return &metadata, nil
}
// concludeGzipRead should be invoked after you think you've consumed all of
// the data from the gzip stream. It will error if the stream was corrupt.View on GitHub (pinned to 482b49bf1a)
Solutions
- Inspect the first bytes of the input — valid gzip starts with 1f 8b; if you see '<html>' or JSON, the transport returned an error page, fix auth/proxy/URL.
- Re-download or re-create the snapshot; the source file is truncated or corrupt.
- Verify the client code streams the raw response body (no intermediate transformation/decoding of the body).
- Use the snapshot's checksum header to validate integrity before passing the stream to CopySnapshot.
Example fix
// before
resp, _ := http.Get(url)
meta, err := CopySnapshot(resp.Body, dest) // body may be an HTML error page
// after
if resp.StatusCode != http.StatusOK { return fmt.Errorf("snapshot fetch failed: %s", resp.Status) }
meta, err := CopySnapshot(resp.Body, dest) Defensive patterns
Strategy: validation
Validate before calling
func isGzip(r io.Reader) (bool, error) {
magic := make([]byte, 2)
if _, err := io.ReadFull(r, magic); err != nil { return false, err }
return magic[0] == 0x1f && magic[1] == 0x8b, nil
} Try / catch
meta, err := CopySnapshot(in, dest)
if err != nil && strings.Contains(err.Error(), "failed to decompress snapshot") {
return fmt.Errorf("input is not a gzip snapshot stream: %w", err)
} Prevention
- Always check HTTP status before streaming a response body into CopySnapshot.
- Validate the snapshot's checksum header before consuming the stream.
- Buffer/peek the first bytes to confirm gzip magic on untrusted inputs.
- Ensure proxies/auth layers return errors out-of-band, not as body content.
When it happens
Trigger: Calling CopySnapshot (directly or via Verify) with a reader whose first bytes are not a valid gzip header: truncated/empty response, HTML error page from a proxy, wrong endpoint, or data that was stored/downloaded without its gzip envelope.
Common situations: Reverse proxy or LB returning a 403/502 HTML page instead of the snapshot bytes; snapshot URL saved to disk and replayed with extra bytes; interrupted download truncating the gzip stream; TLS/auth problems yielding an error page body.
Related errors
- failed to read snapshot: %w
- failed to read snapshot hashes: %v
- failed to compress snapshot file: %v
- failed to read snapshot file: %v
- failed to read snapshot: %w
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/268f88e5b56cb230.
Report an issue: GitHub.