{"record":{"id":"c195b227f0e9ffe8","repo":"hashicorp/nomad","slug":"failed-to-decode-snapshot-metadata-v","errorCode":null,"errorMessage":"failed to decode snapshot metadata: %v","messagePattern":"failed to decode snapshot metadata: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/snapshot/archive.go","lineNumber":212,"sourceCode":"\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)\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.","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/snapshot/archive.go#L194-L230","documentation":"This error is returned when the meta.json bytes read from the snapshot tar archive fail json.Unmarshal into *raft.SnapshotMeta. Unlike [1942], the stream read succeeded but the bytes are not valid JSON matching the expected raft.SnapshotMeta schema. This means the snapshot's metadata section is corrupt or was produced by an incompatible format.","triggerScenarios":"read() (via Restore/CopySnapshot/snapshot Open) on a snapshot whose meta.json entry contains malformed, truncated, or non-JSON bytes — corruption of the archive contents, a mismatched/custom archive, or manual tampering with the tar entries.","commonSituations":"Snapshot file partially corrupted after a crash during copy; hand-assembled or edited snapshot archives; restoring an artifact that is not a Consul snapshot (e.g. a generic Raft snapshot of different vintage or another tool's tar) into Consul; bit-rot on backup media.","solutions":["Validate the snapshot with 'consul snapshot inspect <file>'; if it fails, the snapshot is corrupt","Re-take the snapshot from a healthy raft peer / re-download from the source","Do not hand-edit snapshot archives; meta.json must be a valid raft.SnapshotMeta JSON document","Verify checksums of the backup file against known-good values to rule out transfer corruption"],"exampleFix":"// before\nf, _ := os.Open(backupPath)\nerr := snapshot.Restore(logOut, f)\n// after\nf, _ := os.Open(backupPath)\nif err := snapshot.Verify(f); err != nil {\n    return fmt.Errorf(\"backup at %s is corrupt; use a known-good snapshot: %w\", backupPath, err)\n}\n_, _ = f.Seek(0, 0)\nreturn snapshot.Restore(logOut, f)","handlingStrategy":"validation","validationCode":"// validate before restoring: 'consul snapshot inspect' equivalent check\nfunc validateSnapshotFile(path string) error {\n    f, err := os.Open(path)\n    if err != nil { return err }\n    defer f.Close()\n    _, err = snapshot.Verify(f) // fails fast on corrupt meta.json\n    return err\n}","typeGuard":"func isMetaDecodeErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"failed to decode snapshot metadata\")\n}","tryCatchPattern":"if err := snapshot.Restore(logOut, snap); err != nil {\n    if strings.Contains(err.Error(), \"failed to decode snapshot metadata\") {\n        return fmt.Errorf(\"snapshot meta.json is corrupt or from an incompatible format; restore from a known-good snapshot: %w\", err)\n    }\n    return err\n}","preventionTips":["Never hand-edit or rebuild snapshot tar archives","Validate backups with 'consul snapshot inspect' immediately after taking them and after every copy","Store snapshots on media with checksums (or store side-by-side SHA256) to detect bit-rot","Restore only snapshots produced by a compatible Consul/Raft version"],"tags":["go","json","snapshot","corruption"],"backgroundTag":"json-unmarshal-failed","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"}