{"record":{"id":"c08ca20caa6cc276","repo":"hashicorp/nomad","slug":"failed-to-read-snapshot-hashes-v","errorCode":null,"errorMessage":"failed to read snapshot hashes: %v","messagePattern":"failed to read snapshot hashes: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/snapshot/archive.go","lineNumber":222,"sourceCode":"\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)\n\t\t\t}\n\n\t\tcase \"state.bin\":\n\t\t\tif _, err := io.Copy(io.MultiWriter(snap, snapHash), archive); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to read or write snapshot data: %v\", err)\n\t\t\t}\n\n\t\tcase \"SHA256SUMS\":\n\t\t\tif _, err := io.Copy(&shaBuffer, archive); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to read snapshot hashes: %v\", err)\n\t\t\t}\n\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"unexpected file %q in snapshot\", hdr.Name)\n\t\t}\n\t}\n\n\t// Verify all the hashes.\n\tif err := hl.DecodeAndVerify(&shaBuffer); err != nil {\n\t\treturn fmt.Errorf(\"failed checking integrity of snapshot: %v\", err)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":204,"sourceCodeEnd":237,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/snapshot/archive.go#L204-L237","documentation":"During snapshot archive parsing, the SHA256SUMS entry inside the tar could not be read from the underlying stream. The library throws this when io.Copy of the checksum file into an in-memory buffer fails, which almost always means the gzip/tar stream is corrupt or truncated, or the destination state (temp files, disk) is failing. It is wrapped by CopySnapshot/Restore as \"failed to read snapshot file\".","triggerScenarios":"Calling snapshot.Verify, snapshot.CopySnapshot, or snapshot.Restore on a snapshot whose tar archive's SHA256SUMS member cannot be read: truncated download, corrupt gzip stream mid-checksums-file, or an I/O error on the source reader (e.g. failed HTTP body read).","commonSituations":"Downloaded snapshot files cut off by network interruptions; snapshots transferred via FTP in ASCII mode; storage backend (S3/NFS/disk) returning short reads; restoring an old or hand-edited snapshot archive.","solutions":["Re-obtain the snapshot file from a healthy source (leader API /v1/agent/snapshot or a verified backup) since the current one is likely truncated or corrupt.","Verify the file's integrity before restoring: gunzip -t snapshot.gz (or snapshot.Verify in Go) and compare its sha-256 checksum header against the transfer.","Check disk space and I/O health on the node performing the restore; a failing temp disk can abort the read pipeline.","If transferring manually, re-download in binary mode over a resumable protocol (HTTPS/S3) and compare sizes/checksums with the leader's copy."],"exampleFix":"// before: restoring a partially downloaded snapshot directly\nf, _ := os.Open(\"snapshot.bin\")\nsnapshot.Restore(logger, f, r)\n\n// after: validate first, fall back to re-fetch if corrupt\nf, _ := os.Open(\"snapshot.bin\")\nif _, err := snapshot.Verify(f); err != nil {\n    f.Close()\n    return fmt.Errorf(\"snapshot corrupt, re-download required: %w\", err)\n}\nf.Seek(0, 0)\nsnapshot.Restore(logger, f, r)","handlingStrategy":"validation","validationCode":"// Validate the snapshot stream before restore/copy\nfunc validateSnapshot(in io.Reader) error {\n    f, ok := in.(*os.File)\n    if !ok || f == nil {\n        return fmt.Errorf(\"need a seekable snapshot file\")\n    }\n    if st, err := f.Stat(); err != nil || st.Size() == 0 {\n        return fmt.Errorf(\"snapshot file empty or unreadable\")\n    }\n    if _, err := snapshot.Verify(f); err != nil {\n        return fmt.Errorf(\"snapshot stream corrupt: %w\", err)\n    }\n    f.Seek(0, 0)\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always transfer snapshots in binary mode over checksum-verifying protocols (HTTPS, S3).","Compare file size and sha-256 of the snapshot archive between source and destination before restoring.","Test-verify every snapshot (snapshot.Verify / nomad operator snapshot inspect) before storing it as a backup."],"tags":["go","io","snapshot","corruption"],"backgroundTag":"file-truncated-or-corrupt","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"}