juicedata/juicefs · error
failed to read footer: err %w, read len %d, expect len %d
Error message
failed to read footer: err %w, read len %d, expect len %d
What it means
BakFooter.Unmarshal reads the footer payload of a JuiceFS metadata backup file. After seeking back by the 8-byte big-endian footer length stored at EOF, it reads h.Len bytes containing the protobuf-encoded Footer message. This error is returned when the io.ReadSeeker fails to supply those bytes: the read errored and the number of bytes read does not match h.Len.
Source
Thrown at pkg/meta/backup.go:211
return fmt.Errorf("failed to write footer length: err %w, write len %d, expect len 8", err, n)
}
return nil
}
func (h *BakFooter) Unmarshal(r io.ReadSeeker) error {
lenSize := int64(unsafe.Sizeof(h.Len))
_, _ = r.Seek(-lenSize, io.SeekEnd)
data := make([]byte, lenSize)
if n, err := r.Read(data); err != nil && n != int(lenSize) {
return fmt.Errorf("failed to read footer length: err %w, read len %d, expect len %d", err, n, lenSize)
}
h.Len = binary.BigEndian.Uint64(data)
_, _ = r.Seek(-int64(h.Len)-lenSize, io.SeekEnd)
data = make([]byte, h.Len)
if n, err := r.Read(data); err != nil && n != int(h.Len) {
return fmt.Errorf("failed to read footer: err %w, read len %d, expect len %d", err, n, h.Len)
}
h.Msg = &pb.Footer{}
if err := proto.Unmarshal(data, h.Msg); err != nil {
return fmt.Errorf("failed to unmarshal footer: %w", err)
}
return nil
}
type BakSegment struct {
typ uint32
len uint64
val proto.Message
}
func (s *BakSegment) Name() string {
if name, ok := SegType2Name[int(s.typ)]; ok {
return nameView on GitHub (pinned to c9a67b23e8)
Solutions
- Re-obtain the backup file from a known-good source (re-dump metadata with `juicefs dump`) — a footer read failure almost always means the file is truncated or corrupt.
- Verify the file size matches the expected size (e.g. compare against the source checksum) to confirm truncation before spending time on recovery.
- Ensure the file is not still being written: wait for `juicefs dump` to complete or check for a completed/part marker before reading the footer.
- Check the underlying storage health/permissions if the wrapped err indicates I/O failure (disk, NFS, object storage credentials).
- Confirm the reader passed to ReadFooter implements io.ReadSeeker correctly and its current position is at EOF (offsets managed outside this function can misalign the seek).
Example fix
// before: reading a possibly-incomplete backup
f, _ := os.Open("meta.backup")
footer, err := format.ReadFooter(f)
// after: verify size/checksum before reading
f, _ := os.Open("meta.backup")
fi, _ := f.Stat()
if fi.Size() < 12 {
return fmt.Errorf("backup too small to contain a footer: %d bytes", fi.Size())
}
if got := sha256sum("meta.backup"); got != expectedChecksum {
return fmt.Errorf("backup corrupt: checksum mismatch")
}
footer, err := format.ReadFooter(f) Defensive patterns
Strategy: try-catch
Validate before calling
fi, err := os.Stat(backupPath)
if err != nil { return err }
if fi.Size() < 12 { return fmt.Errorf("backup too small for a footer: %d bytes", fi.Size()) }
// optionally: verify checksum before loading
if !checksumMatches(backupPath, expected) { return errors.New("backup corrupt; re-dump") } Type guard
func canReadFooter(f io.ReadSeeker) bool {
fi, ok := f.(interface{ Stat() (os.FileInfo, error) })
if !ok { return true } // non-file readers: cannot pre-check
info, err := fi.Stat()
return err == nil && info.Size() >= 12
} Try / catch
footer, err := format.ReadFooter(f)
if err != nil {
if strings.Contains(err.Error(), "failed to read footer") {
return fmt.Errorf("backup file truncated or unreadable, re-run juicefs dump: %w", err)
}
return err
} Prevention
- Always verify backup file size/checksum before restoring.
- Only read backups whose producing dump process exited successfully.
- Store backups with a completion marker or .part suffix until dump finishes.
- Check disk/storage health when backups live on network mounts.
When it happens
Trigger: Calling ReadFooter (via BakFormat.ReadFooter) on a file whose last 8 bytes encode a footer length larger than the actual remaining data, or on a truncated/corrupted backup file, or on a reader that returns an I/O error mid-read (e.g. network storage disconnect, file deleted/rotated while open).
Common situations: Restoring from a metadata backup that was truncated by a failed download or interrupted rsync; reading a backup still being written by another `juicefs dump` process; a corrupt footer-length prefix (bit rot, wrong file) making h.Len impossibly large; an S3/NFS-backed seekable reader hitting an I/O error.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- failed to read footer length: err %w, read len %d, expect le
- failed to unmarshal footer: %w
- failed to write segment length %s: %w
- unknown message type %d
- failed to find message %s's type: %v
AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06).
Data as JSON: /api/errors/828c2e6efcf8ee8b.
Report an issue: GitHub.