juicedata/juicefs · error

failed to read footer: %w

Error message

failed to read footer: %w

What it means

`showBakSummary` reads the backup file's footer via meta.BakFormat.ReadFooter(fp). The footer is a length-prefixed protobuf record at the end of the file containing the backup version and per-segment info. If the footer cannot be read or decoded (corrupt, truncated, or not a JuiceFS backup file), the error is wrapped as `failed to read footer: <error>`.

Source

Thrown at cmd/load.go:283

	defer fp.Close()

	if !ctx.IsSet("offset") {
		return showBakSummary(ctx, fp, false)
	}

	offset := ctx.Int64("offset")
	if offset == -1 {
		return showBakSummary(ctx, fp, true)
	}

	return showBakDetail(ctx, fp, offset)
}

func showBakSummary(ctx *cli.Context, fp *os.File, withOffset bool) error {
	bak := &meta.BakFormat{}
	footer, err := bak.ReadFooter(fp)
	if err != nil {
		return fmt.Errorf("failed to read footer: %w", err)
	}

	fmt.Printf("Backup Version: %d\n", footer.Msg.Version)
	data := make([][]string, 0, len(footer.Msg.Infos))
	for name, info := range footer.Msg.Infos {
		if withOffset {
			data = append(data, []string{name, fmt.Sprintf("%d", info.Num), fmt.Sprintf("%d", info.Offset)})
		} else {
			data = append(data, []string{name, fmt.Sprintf("%d", info.Num)})
		}
	}
	sort.Slice(data, func(i, j int) bool {
		return data[i][0] < data[j][0]
	})

	if withOffset {
		fmt.Println(strings.Repeat("-", 34))
		fmt.Printf("%-10s| %-10s| %-10s\n", "Name", "Num", "Offset")

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Verify the file is a complete JuiceFS binary backup (check size against the source, re-download if needed)
  2. Confirm you are not passing a plain JSON dump — footers only exist in binary backups
  3. Decompress first if the backup is gzipped
  4. Re-create the backup with `juicefs dump --backup` and retry; if the version is newer, upgrade your JuiceFS client

Example fix

// before
juicefs load --stat meta.sqlite3 backup.json.gz      # still gzipped
// after
gunzip backup.json.gz && juicefs load --stat meta.sqlite3 backup.json
Defensive patterns

Strategy: validation

Validate before calling

fi, err := os.Stat(backupPath)
if err != nil { return err }
if fi.Size() < 64 { return fmt.Errorf("file too small to be a backup: %s", backupPath) }
if hasGzipMagic(backupPath) { return fmt.Errorf("decompress before inspecting") }

Try / catch

if err := showSummary(fp); err != nil {
    if strings.Contains(err.Error(), "failed to read footer") {
        log.Fatalf("truncated/corrupt or non-binary backup: %v", err)
    }
}

Prevention

When it happens

Trigger: Inspecting (`statBak`/summary) a file that is not a JuiceFS binary backup (e.g. a plain JSON dump, or a text file), or a backup that was truncated by an interrupted dump/transfer, or one whose trailing bytes were cut off.

Common situations: Downloading the backup from object storage incompletely; opening a gzipped backup without decompressing; a backup written by a much newer JuiceFS client with an incompatible footer version; corrupted file from a failed rsync.

Understand the failure class

Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.

Related errors


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