{"record":{"id":"cba0d88805097c10","repo":"hashicorp/nomad","slug":"failed-reading-snapshot-v","errorCode":null,"errorMessage":"failed reading snapshot: %v","messagePattern":"failed reading snapshot: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/snapshot/archive.go","lineNumber":194,"sourceCode":"\t// Create a hash list that we will use to compare with the SHA256SUMS\n\t// file in the archive.\n\thl := newHashList()\n\n\t// Populate the hashes for all the files we expect to see. The check at\n\t// the end will make sure these are all present in the SHA256SUMS file\n\t// and that the hashes match.\n\tmetaHash := hl.Add(\"meta.json\")\n\tsnapHash := hl.Add(\"state.bin\")\n\n\t// Look through the archive for the pieces we care about.\n\tvar shaBuffer bytes.Buffer\n\tfor {\n\t\thdr, err := archive.Next()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed reading snapshot: %v\", err)\n\t\t}\n\n\t\tswitch hdr.Name {\n\t\tcase \"meta.json\":\n\t\t\t// Previously we used json.Decode to decode the archive stream. There are\n\t\t\t// edgecases in which it doesn't read all the bytes from the stream, even\n\t\t\t// though the json object is still being parsed properly. Since we\n\t\t\t// simultaneously feeded everything to metaHash, our hash ended up being\n\t\t\t// different than what we calculated when creating the snapshot. Which in\n\t\t\t// turn made the snapshot verification fail. By explicitly reading the\n\t\t\t// whole thing first we ensure that we calculate the correct hash\n\t\t\t// independent of how json.Decode works internally.\n\t\t\tbuf, err := io.ReadAll(io.TeeReader(archive, metaHash))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to read snapshot metadata: %v\", err)\n\t\t\t}\n\t\t\tif err := json.Unmarshal(buf, &metadata); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to decode snapshot metadata: %v\", err)","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/snapshot/archive.go#L176-L212","documentation":"This error is returned from read() when archive.Next() on the tar reader fails with an error other than io.EOF, i.e. the snapshot stream is not a readable tar archive. It means the bytes being read as a Consul/Raft snapshot are corrupted, truncated, or not a snapshot at all. The wrapped %v contains the tar package's underlying parse error.","triggerScenarios":"Calling read() (via Snapshot.Open/Restore/CopySnapshot/API snapshot download) on a stream where tar.Reader.Next() fails: the snapshot file was truncated, the tar header block is corrupt, the response is an HTML/JSON error page instead of a tarball, or bytes were mangled in transit (proxy/TLS termination issues).","commonSituations":"Downloading a snapshot from a load balancer that returns a 502/503 error body; snapshot file truncated by an incomplete copy; restoring from a snapshot file that was gzip-compressed but not decompressed first; corrupted backup on disk.","solutions":["Verify the file/stream is an unmodified Consul snapshot: run 'consul snapshot inspect <file>' to validate it","Check you are not double-decompressing or feeding a gzip stream where raw tar is expected","Re-download or re-take the snapshot; the source copy is likely truncated or corrupt","Check proxies/LBs that may have replaced the body with an error page — compare content-length and content-type","Confirm the snapshot was taken with a compatible Consul version"],"exampleFix":"// before\nsnap, err := agent.Snapshot().Open()\nrestored, err := snapshot.Restore(logOut, snap)\n// after\nsnap, err := agent.Snapshot().Open()\nif err != nil { return err }\n// sanity-check the stream is a tar before restoring\nvar probe [512]byte\nif _, err := io.ReadFull(snap, probe[:]); err != nil || !bytes.Equal(probe[257:262], []byte(\"ustar\")) {\n    return fmt.Errorf(\"stream is not a valid snapshot tar archive\")\n}","handlingStrategy":"validation","validationCode":"func looksLikeTar(r io.Reader) (bool, error) {\n    hdr := make([]byte, 512)\n    if _, err := io.ReadFull(r, hdr); err != nil {\n        return false, err\n    }\n    // ustar magic at offset 257\n    return string(hdr[257:262]) == \"ustar\", nil\n}","typeGuard":"func isInvalidSnapshotErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"failed reading snapshot\")\n}","tryCatchPattern":"if err := snapshot.Restore(logOut, snap); err != nil {\n    if strings.Contains(err.Error(), \"failed reading snapshot\") {\n        return fmt.Errorf(\"snapshot source is corrupt or not a tar archive; re-take snapshot: %w\", err)\n    }\n    return err\n}","preventionTips":["Run 'consul snapshot inspect' on every backup before relying on it","Do not gzip/transform snapshot bytes in transit without symmetric decompression","Verify content-type/content-length when downloading snapshots through proxies","Keep multiple generations of snapshots so a corrupt copy can be replaced"],"tags":["go","tar","snapshot","corruption"],"backgroundTag":"invalid-tar-archive","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"}