{"record":{"id":"19c1ede1296cd1e5","repo":"juicedata/juicefs","slug":"expect-owner-nobody-but-got-s","errorCode":null,"errorMessage":"expect owner nobody but got %s","messagePattern":"expect owner nobody but got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cmd/objbench.go","lineNumber":1105,"sourceCode":"\t\t\t\treturn fmt.Errorf(\"the content of the multipart upload file is incorrect\")\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\treturn utils.ErrNotSUP\n\t})\n\n\tfunFSCase(\"change owner/group\", func() error {\n\t\tif (strings.HasPrefix(blob.String(), \"file://\") || strings.HasPrefix(blob.String(), \"jfs://\")) && os.Getuid() != 0 {\n\t\t\treturn errors.New(\"root required\")\n\t\t}\n\t\tif err := fi.Chown(key, \"nobody\", groupName); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to chown object %s\", err)\n\t\t}\n\t\tif objInfo, err := blob.Head(ctx, key); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to head object %s\", err)\n\t\t} else if info, ok := objInfo.(object.File); ok {\n\t\t\tif info.Owner() != \"nobody\" {\n\t\t\t\treturn fmt.Errorf(\"expect owner nobody but got %s\", info.Owner())\n\t\t\t}\n\t\t\tif info.Group() != groupName {\n\t\t\t\treturn fmt.Errorf(\"expect group %s but got %s\", groupName, info.Group())\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\n\tfunFSCase(\"change permission\", func() error {\n\t\tif err := fi.Chmod(key, 0777); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif objInfo, err := blob.Head(ctx, key); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to head object %s\", err)\n\t\t} else if info, ok := objInfo.(object.File); ok {\n\t\t\tif info.Mode()&0xFFF != 0777 {\n\t\t\t\treturn fmt.Errorf(\"expect mode %o but got %o\", 0777, info.Mode())\n\t\t\t}","sourceCodeStart":1087,"sourceCodeEnd":1123,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/cmd/objbench.go#L1087-L1123","documentation":"Verification failure in the 'change owner/group' case: after Chown to 'nobody', Head reports the object's Owner is still not 'nobody'. This means the ownership change did not take effect (silently ignored by the backend) or the metadata read is stale. It only fires when the object metadata is of type object.File (backends without File metadata skip the check).","triggerScenarios":"fi.Chown succeeded but info.Owner() != \"nobody\" in the subsequent Head: the backend accepted but ignored the chown, ownership mapping is not persisted, or a stale/cached metadata response was returned.","commonSituations":"Backends that emulate File metadata but don't persist owner changes, FUSE mounts where only root can chown yet the call silently no-ops, cached Head results on gateway/proxy layers.","solutions":["Verify the backend actually persists chown (test manually with `chown` + `ls -l`).","Run as root — non-root chown may be silently dropped or fail in ways the check exposes.","Invalidate/disable metadata caches between Chown and Head.","Skip the ownership case for backends that don't support owner metadata."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"if objInfo, err := blob.Head(ctx, key); err == nil {\n\tif _, ok := objInfo.(object.File); !ok {\n\t\treturn utils.ErrNotSUP // backend has no owner metadata to verify\n\t}\n}","typeGuard":"func asFile(o object.ObjectMetadata) (object.File, bool) {\n\tf, ok := o.(object.File)\n\treturn f, ok\n}","tryCatchPattern":null,"preventionTips":["Only assert ownership on backends returning object.File metadata","Run as root so chown actually persists","Invalidate caches between Chown and Head","Manually verify backend persists owner changes before relying on it"],"tags":["permissions","ownership","verification"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}