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 name

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. 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.
  2. Verify the file size matches the expected size (e.g. compare against the source checksum) to confirm truncation before spending time on recovery.
  3. 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.
  4. Check the underlying storage health/permissions if the wrapped err indicates I/O failure (disk, NFS, object storage credentials).
  5. 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

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


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/828c2e6efcf8ee8b. Report an issue: GitHub.