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 nilView on GitHub (pinned to c9a67b23e8)
Solutions
- Use an object storage backend that persists POSIX mode metadata (e.g. JuiceFS-aware storage).
- If backend cannot store modes, treat this case as informational; check whether objbench reports it as a warning rather than a data-integrity issue.
- Re-run with a local storage or a backend known to support mode preservation to confirm the benchmark harness itself is fine.
- 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
- Prefer backends that persist POSIX mode metadata when mode fidelity matters.
- Expect mode checks to be skipped on plain S3-style backends.
- Confirm the backend sets mode metadata headers on PUT.
- Treat chmod round-trip failures as backend limitations, not data corruption.
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
- ErrNotSUP
- Failed to put: %s
- failed to get the first byte:, expect "h", but got %q, error
- failed to get the last byte: expect "o", but got %q, error:
- failed to get the last three bytes: expect "llo", but got %q
AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06).
Data as JSON: /api/errors/1b38f3b9f9d61977.
Report an issue: GitHub.