{"record":{"id":"be1a38759dc7aa1e","repo":"juicedata/juicefs","slug":"jfsobjects-newnslock-the-length-of-the-objects-pa","errorCode":null,"errorMessage":"jfsObjects.NewNSLock: the length of the objects parameter must be 1, current %s","messagePattern":"jfsObjects\\.NewNSLock: the length of the objects parameter must be 1, current (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/gateway/gateway.go","lineNumber":1523,"sourceCode":"\treturn j.getFlockWithTimeOut(ctx, meta.F_RDLCK, timeout)\n}\n\nfunc (j *jfsFLock) RUnlock() {\n\tif j.inode == 0 || j.readonly {\n\t\treturn\n\t}\n\tif errno := j.meta.Flock(mctx, j.inode, j.owner, meta.F_UNLCK, true); errno != 0 {\n\t\tlogger.Errorf(\"failed to release lock for inode %d by owner %d, error : %s\", j.inode, j.owner, errno)\n\t}\n\tj.localLock.RUnlock()\n}\n\nfunc (n *jfsObjects) NewNSLock(bucket string, objects ...string) minio.RWLocker {\n\tif n.gConf.ReadOnly {\n\t\treturn &jfsFLock{readonly: true}\n\t}\n\tif len(objects) != 1 {\n\t\tpanic(fmt.Errorf(\"jfsObjects.NewNSLock: the length of the objects parameter must be 1, current %s\", objects))\n\t}\n\n\tlockfile := path.Join(minio.MinioMetaBucket, minio.MinioMetaLockFile)\n\tvar file *fs.File\n\tvar errno syscall.Errno\n\tfile, errno = n.fs.Open(mctx, lockfile, vfs.MODE_MASK_W)\n\tif errno != 0 && !errors.Is(errno, syscall.ENOENT) {\n\t\tlogger.Errorf(\"failed to open the file to be locked: %s error %s\", lockfile, errno)\n\t\treturn &jfsFLock{}\n\t}\n\tif errors.Is(errno, syscall.ENOENT) {\n\t\tif file, errno = n.fs.Create(mctx, lockfile, 0666, n.gConf.Umask); errno != 0 {\n\t\t\tif errors.Is(errno, syscall.EEXIST) {\n\t\t\t\tif file, errno = n.fs.Open(mctx, lockfile, vfs.MODE_MASK_W); errno != 0 {\n\t\t\t\t\tlogger.Errorf(\"failed to open the file to be locked: %s error %s\", lockfile, errno)\n\t\t\t\t\treturn &jfsFLock{}\n\t\t\t\t}\n\t\t\t} else {","sourceCodeStart":1505,"sourceCodeEnd":1541,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/gateway/gateway.go#L1505-L1541","documentation":"NewNSLock implements minio's RWLocker interface and requires the namespace lock to be scoped to exactly one object path. The JuiceFS gateway cannot express a multi-object or zero-object lock in its single-lockfile flock implementation, so it panics when the caller violates that contract.","triggerScenarios":"Calling jfsObjects.NewNSLock with zero objects (NewNSLock(bucket)) or more than one object (NewNSLock(bucket, obj1, obj2)); internally this happens only if a minio Multipart/erasure-lock caller passes multiple lock targets.","commonSituations":"Embedding jfsObjects into a custom S3 gateway layer that calls NewNSLock directly with multiple object names; version drift between minio/pkg and JuiceFS changing how many lock targets are passed; readonly mode masking the bug until a writable mount is used.","solutions":["Pass exactly one object path when acquiring a namespace lock","If multiple objects need locking, call NewNSLock once per object and acquire each lock separately","Verify the vendored minio version matches what the JuiceFS gateway expects (go.mod) so internal lock calls pass 1 object"],"exampleFix":"// before\nlocker := jfs.NewNSLock(bucket, obj1, obj2)\n// after\nl1 := jfs.NewNSLock(bucket, obj1)\nl2 := jfs.NewNSLock(bucket, obj2)","handlingStrategy":"validation","validationCode":"if len(objects) != 1 {\n    return fmt.Errorf(\"NewNSLock requires exactly 1 object, got %d\", len(objects))\n}\nlocker := jfs.NewNSLock(bucket, objects[0])","typeGuard":"func validNSLockArgs(objects []string) bool { return len(objects) == 1 }","tryCatchPattern":"// panic-based: wrap call site\ndefer func() {\n    if r := recover(); r != nil {\n        log.Printf(\"NewNSLock misuse: %v\", r)\n    }\n}()","preventionTips":["Always lock one object per NewNSLock call","Add an assertion/unit test on custom gateway code that calls NewNSLock","Keep the vendored minio version in sync with JuiceFS expectations"],"tags":["gateway","s3","locking","panic"],"backgroundTag":"invalid-argument-value","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"}