{"record":{"id":"1d120a4c4f86d758","repo":"hashicorp/nomad","slug":"failed-to-read-snapshot-file-v","errorCode":null,"errorMessage":"failed to read snapshot file: %v","messagePattern":"failed to read snapshot file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/snapshot/snapshot.go","lineNumber":212,"sourceCode":"\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.\n//\n// The docs for gzip.Reader say: \"Clients should treat data returned by Read as\n// tentative until they receive the io.EOF marking the end of the data.\"\nfunc concludeGzipRead(decomp *gzip.Reader) error {\n\textra, err := io.ReadAll(decomp) // ReadAll consumes the EOF\n\tif err != nil {\n\t\treturn err","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/snapshot/snapshot.go#L194-L230","documentation":"CopySnapshot wraps errors from read(), which parses the raft.SnapshotMeta header and streams the (discarded) snapshot payload into dest. This means the gzip stream opened fine, but the archive inside is truncated, corrupt, or otherwise unreadable.","triggerScenarios":"CopySnapshot/Verify where read(decomp, &metadata, dest) fails: truncated snapshot archive, corrupt msgpack header, dest write errors while draining payload, or mid-stream gzip corruption (CRC/length mismatch).","commonSituations":"Snapshot files damaged by interrupted transfers or failing storage; byte offsets shifted by manual edits; dest WriteCloser failing (disk full on the receiving side) during the copy.","solutions":["Compare the received file size/checksum against the source snapshot's checksum header to confirm truncation.","Re-transfer or re-create the snapshot from a healthy leader/node.","Check the wrapped %v cause: if it's a dest write error, free space/fix permissions on the destination.","Run Verify against the candidate snapshot before attempting a restore to catch corruption early."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// verify integrity first; Verify uses CopySnapshot and will surface the same corruption\nif err := snapshot.Verify(in); err != nil {\n\treturn fmt.Errorf(\"snapshot invalid, refusing to use: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"meta, err := CopySnapshot(in, dest)\nif err != nil && strings.Contains(err.Error(), \"failed to read snapshot file\") {\n\treturn fmt.Errorf(\"snapshot archive corrupt or truncated: %w\", err)\n}","preventionTips":["Compare received size/checksum against the source before use.","Re-transfer on any checksum mismatch instead of forcing restore.","Keep the dest WriteCloser healthy (space, permissions) — its write errors surface here.","Run Verify on candidate snapshots before any restore workflow."],"tags":["snapshot","corruption","truncated","io"],"backgroundTag":"snapshot-archive-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"}