juicedata/juicefs · warning

expect mode %o but got %o

Error message

expect mode %o but got %o

What it means

objbench 'change permission' test compares the mode returned by Head (cast to object.File) against the expected 0777 that was just applied via fi.Chmod(key, 0777). It fires when the backend does not round-trip POSIX permission bits on objects, masking the mode bits with &0xFFF. Note the format arguments are swapped in the message (expect/got order reversed in practice).

Source

Thrown at cmd/objbench.go:1122

			if info.Owner() != "nobody" {
				return fmt.Errorf("expect owner nobody but got %s", info.Owner())
			}
			if info.Group() != groupName {
				return fmt.Errorf("expect group %s but got %s", groupName, info.Group())
			}
		}
		return nil
	})

	funFSCase("change permission", func() error {
		if err := fi.Chmod(key, 0777); err != nil {
			return err
		}
		if objInfo, err := blob.Head(ctx, key); err != nil {
			return fmt.Errorf("failed to head object %s", err)
		} else if info, ok := objInfo.(object.File); ok {
			if info.Mode()&0xFFF != 0777 {
				return fmt.Errorf("expect mode %o but got %o", 0777, info.Mode())
			}
		}
		return nil
	})

	funFSCase("change mtime", func() error {
		mtime := time.Now().Add(-10 * time.Minute)
		if err := fi.Chtimes(key, mtime); err != nil {
			return fmt.Errorf("failed to chtimes %s", err)
		}
		if objInfo, err := blob.Head(ctx, key); err != nil {
			return fmt.Errorf("failed to head object %s", err)
		} else {
			if objInfo.Mtime().Before(mtime.Add(-2*time.Second)) || objInfo.Mtime().After(mtime.Add(2*time.Second)) {
				return fmt.Errorf("mtime deviation is too large, the actual mtime is %s but got %s", mtime.Format(time.RFC3339), objInfo.Mtime().Format(time.RFC3339))
			}
		}
		return nil

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Use an object storage backend that persists POSIX mode metadata (e.g. JuiceFS-aware storage).
  2. If backend cannot store modes, treat this case as informational; check whether objbench reports it as a warning rather than a data-integrity issue.
  3. Re-run with a local storage or a backend known to support mode preservation to confirm the benchmark harness itself is fine.
  4. Verify via the actual bucket that mode metadata headers (e.g. x-amz-meta-mode) are being set.

Example fix

// message currently swaps expect/got order
return fmt.Errorf("expect mode %o but got %o", 0777, info.Mode())
// clearer ordering
return fmt.Errorf("expect mode %o but got %o", 0777&0xFFF, info.Mode()&0xFFF)
Defensive patterns

Strategy: type-guard

Validate before calling

info, ok := objInfo.(object.File)
if !ok { return nil } // backend doesn't report mode; skip check

Type guard

func fileMode(obj object.Object) (os.FileMode, bool) {
    f, ok := obj.(object.File)
    if !ok { return 0, false }
    return f.Mode() & 0xFFF, true
}

Prevention

When it happens

Trigger: fi.Chmod(key, 0777) succeeds, blob.Head returns an object.File whose Mode()&0xFFF != 0777.

Common situations: Object storage backends (S3, OSS, etc.) that ignore or don't persist chmod/mode metadata; using a backend where Head does not return x-amz-mode metadata.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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