{"record":{"id":"69b39141407e669c","repo":"juicedata/juicefs","slug":"failed-to-unmarshal-footer-w","errorCode":null,"errorMessage":"failed to unmarshal footer: %w","messagePattern":"failed to unmarshal footer: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/meta/backup.go","lineNumber":216,"sourceCode":"func (h *BakFooter) Unmarshal(r io.ReadSeeker) error {\n\tlenSize := int64(unsafe.Sizeof(h.Len))\n\t_, _ = r.Seek(-lenSize, io.SeekEnd)\n\n\tdata := make([]byte, lenSize)\n\tif n, err := r.Read(data); err != nil && n != int(lenSize) {\n\t\treturn fmt.Errorf(\"failed to read footer length: err %w, read len %d, expect len %d\", err, n, lenSize)\n\t}\n\n\th.Len = binary.BigEndian.Uint64(data)\n\t_, _ = r.Seek(-int64(h.Len)-lenSize, io.SeekEnd)\n\tdata = make([]byte, h.Len)\n\tif n, err := r.Read(data); err != nil && n != int(h.Len) {\n\t\treturn fmt.Errorf(\"failed to read footer: err %w, read len %d, expect len %d\", err, n, h.Len)\n\t}\n\n\th.Msg = &pb.Footer{}\n\tif err := proto.Unmarshal(data, h.Msg); err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal footer: %w\", err)\n\t}\n\treturn nil\n}\n\ntype BakSegment struct {\n\ttyp uint32\n\tlen uint64\n\tval proto.Message\n}\n\nfunc (s *BakSegment) Name() string {\n\tif name, ok := SegType2Name[int(s.typ)]; ok {\n\t\treturn name\n\t}\n\treturn fmt.Sprintf(\"type-%d\", s.typ)\n}\n\nfunc (s *BakSegment) String() string {","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/meta/backup.go#L198-L234","documentation":"After successfully reading h.Len bytes of footer payload from the backup file, BakFooter.Unmarshal attempts to proto.Unmarshal them into a pb.Footer message. This error means the bytes are not a valid protobuf Footer encoding — the data is corrupt, wrong, or the length prefix pointed at the wrong bytes.","triggerScenarios":"Reading a backup file whose footer region was overwritten or partially written (crash during dump before fsync); byte-shifted files (footer length parsed from wrong offset); passing a non-backup file to ReadFooter; incompatible pb.Footer schema/round-trip through an older writer writing a different format.","commonSituations":"Manual edits or binary patches to a backup file; rsync/S3 download that altered or truncated content without changing the length trailer; attempting to restore a backup produced by a much older/newer JuiceFS version with a different footer layout; corrupted volume on which the backup resides.","solutions":["Regenerate the backup with `juicefs dump` from the live metadata engine and retry — corrupt protobuf bytes cannot be repaired in place.","Check whether the dump that produced the file completed and was fsynced; discard backups written by processes that crashed mid-dump.","Verify the file is actually a JuiceFS metadata backup (correct magic/size) and not a different file accidentally passed to ReadFooter.","Compare versions: if the backup came from a different JuiceFS release, use a compatible client version to load it.","Inspect the wrapped err from proto.Unmarshal — a size/resource error hints the h.Len trailer is bogus and the file structure is shifted."],"exampleFix":"// before: no integrity check before loading footer\ndata := downloadBackup(\"s3://bucket/meta.backup\")\nfooter, err := format.ReadFooter(bytes.NewReader(data))\n\n// after: verify integrity first\ndata := downloadBackup(\"s3://bucket/meta.backup\")\nif sha256.Sum256(data) != expectedChecksum {\n    return fmt.Errorf(\"backup corrupt; re-run juicefs dump\")\n}\nfooter, err := format.ReadFooter(bytes.NewReader(data))","handlingStrategy":"try-catch","validationCode":"if sha256sum(backupPath) != expectedChecksum {\n    return errors.New(\"backup content mismatch; regenerate with juicefs dump\")\n}\n// sanity: last 8 bytes (footer len) must fit in file\nsz := fileSize(backupPath)\nflen := readUint64BEAt(backupPath, sz-8)\nif 8+uint64(flen) > sz { return errors.New(\"footer length exceeds file size; file corrupt\") }","typeGuard":"func footerLenFitsInFile(f io.ReaderAt) bool {\n    buf := make([]byte, 8)\n    if _, err := f.ReadAt(buf, fileSize-8); err != nil { return false }\n    flen := binary.BigEndian.Uint64(buf)\n    return 8+flen <= fileSize\n}","tryCatchPattern":"if err := footer.Unmarshal(f); err != nil {\n    var perr *proto.UnmarshalError\n    if errors.As(err, &perr) || strings.Contains(err.Error(), \"failed to unmarshal footer\") {\n        return fmt.Errorf(\"backup footer corrupt — regenerate the backup: %w\", err)\n    }\n    return err\n}","preventionTips":["Store a checksum alongside every backup and verify before load.","Validate the footer-length trailer (last 8 bytes) fits the file size before parsing.","Keep client JuiceFS version compatible with the backup's writer version.","Never hand-edit or binary-patch backup files."],"tags":["protobuf","backup","corrupt-file","unmarshal"],"backgroundTag":"protobuf-unmarshal-failed","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}