{"record":{"id":"268f88e5b56cb230","repo":"hashicorp/nomad","slug":"failed-to-decompress-snapshot-v","errorCode":null,"errorMessage":"failed to decompress snapshot: %v","messagePattern":"failed to decompress snapshot: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/snapshot/snapshot.go","lineNumber":205,"sourceCode":"\tio.Writer\n}\n\nfunc (dc Discard) Close() error { return nil }\n\n// Verify takes the snapshot from the reader and verifies its contents.\nfunc Verify(in io.Reader) (*raft.SnapshotMeta, error) {\n\treturn CopySnapshot(in, Discard{Writer: io.Discard})\n}\n\n// CopySnapshot copies the snapshot content from snapshot archive to dest.\n// It will close the destination once complete.\nfunc CopySnapshot(in io.Reader, dest io.WriteCloser) (*raft.SnapshotMeta, error) {\n\tdefer dest.Close()\n\n\t// Wrap the reader in a gzip decompressor.\n\tdecomp, err := gzip.NewReader(in)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decompress snapshot: %v\", err)\n\t}\n\tdefer decomp.Close()\n\n\t// Read the archive, throwing away the snapshot data.\n\tvar metadata raft.SnapshotMeta\n\tif err := read(decomp, &metadata, dest); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read snapshot file: %v\", err)\n\t}\n\n\tif err := concludeGzipRead(decomp); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &metadata, nil\n}\n\n// concludeGzipRead should be invoked after you think you've consumed all of\n// the data from the gzip stream. It will error if the stream was corrupt.","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/snapshot/snapshot.go#L187-L223","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nresp, _ := http.Get(url)\nmeta, err := CopySnapshot(resp.Body, dest) // body may be an HTML error page\n// after\nif resp.StatusCode != http.StatusOK { return fmt.Errorf(\"snapshot fetch failed: %s\", resp.Status) }\nmeta, err := CopySnapshot(resp.Body, dest)","handlingStrategy":"validation","validationCode":"func isGzip(r io.Reader) (bool, error) {\n\tmagic := make([]byte, 2)\n\tif _, err := io.ReadFull(r, magic); err != nil { return false, err }\n\treturn magic[0] == 0x1f && magic[1] == 0x8b, nil\n}","typeGuard":null,"tryCatchPattern":"meta, err := CopySnapshot(in, dest)\nif err != nil && strings.Contains(err.Error(), \"failed to decompress snapshot\") {\n\treturn fmt.Errorf(\"input is not a gzip snapshot stream: %w\", err)\n}","preventionTips":["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."],"tags":["gzip","snapshot","corruption","io"],"backgroundTag":"invalid-gzip-stream","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}