{"record":{"id":"038f623fe9477416","repo":"juicedata/juicefs","slug":"failed-to-read-footer-length-err-w-read-len-d","errorCode":null,"errorMessage":"failed to read footer length: err %w, read len %d, expect len %d","messagePattern":"failed to read footer length: err %w, read len (.+?), expect len (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/meta/backup.go","lineNumber":204,"sourceCode":"\n\tif n, err := w.Write(data); err != nil && n != len(data) {\n\t\treturn fmt.Errorf(\"failed to write footer data: err %w, write len %d, expect len %d\", err, n, len(data))\n\t}\n\n\th.Len = uint64(len(data))\n\tif n, err := w.Write(binary.BigEndian.AppendUint64(nil, h.Len)); err != nil && n != 8 {\n\t\treturn fmt.Errorf(\"failed to write footer length: err %w, write len %d, expect len 8\", err, n)\n\t}\n\treturn nil\n}\n\nfunc (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","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/meta/backup.go#L186-L222","documentation":"BakFooter.Unmarshal reads the last 8 bytes of the stream to obtain the footer length. If the read errors or returns fewer bytes than requested, the file is too short or unreadable and this wrapped error is returned, preventing footer parsing.","triggerScenarios":"ReadFooter → BakFooter.Unmarshal on a file smaller than 8 bytes, a truncated backup, or an unreadable/corrupt tail region (I/O error from the underlying ReadSeeker).","commonSituations":"Pointing summary/load at a truncated or partially uploaded backup file; a zero-byte or wrong file passed to juicefs load/summary; storage-level corruption at the file tail.","solutions":["Verify the backup file size and integrity; re-transfer it","Regenerate the backup with juicefs dump","Check the source storage for read errors (dmesg, object storage consistency)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fi, err := os.Stat(bakPath)\nif err != nil {\n    return err\n}\nif fi.Size() < 8 {\n    return fmt.Errorf(\"%s too small to contain a footer\", bakPath)\n}","typeGuard":"func plausiblyBackup(path string) bool {\n    fi, err := os.Stat(path)\n    return err == nil && fi.Size() > 8\n}","tryCatchPattern":"footer, err := f.ReadFooter(rs)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to read footer length\") {\n        return fmt.Errorf(\"backup file truncated or unreadable: %w\", err)\n    }\n    return err\n}","preventionTips":["Verify backup file size/checksum after transfer","Avoid partial uploads; upload only completed files","Check storage health if tail reads fail repeatedly"],"tags":["backup","io","read","truncated-file"],"backgroundTag":"file-read-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"}