{"record":{"id":"7b727e7cf3abfb27","repo":"larksuite/cli","slug":"w-lock-s-syscall-v","errorCode":null,"errorMessage":"%w (lock: %s, syscall: %v)","messagePattern":"%w \\(lock: (.+?), syscall: (.+?)\\)","errorType":"exception","errorClass":"ErrHeld","httpStatus":null,"severity":"warning","filePath":"internal/lockfile/lock_unix.go","lineNumber":17,"sourceCode":"// Copyright (c) 2026 Lark Technologies Pte. Ltd.\n// SPDX-License-Identifier: MIT\n\n//go:build !windows\n\npackage lockfile\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"syscall\"\n)\n\nfunc tryLockFile(f *os.File) error {\n\terr := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%w (lock: %s, syscall: %v)\", ErrHeld, f.Name(), err)\n\t}\n\treturn nil\n}\n\nfunc unlockFile(f *os.File) error {\n\treturn syscall.Flock(int(f.Fd()), syscall.LOCK_UN)\n}\n","sourceCodeStart":1,"sourceCodeEnd":25,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/lockfile/lock_unix.go#L1-L25","documentation":"This error signals that an exclusive, non-blocking flock on a lock file failed because another process already holds the lock. It wraps the sentinel ErrHeld with %w so callers can use errors.Is(err, lockfile.ErrHeld) to distinguish 'lock is busy' from other I/O failures; the lock file path and the raw syscall error are embedded for context. It is the Unix implementation (syscall.Flock LOCK_EX|LOCK_NB) of busy-lock detection.","triggerScenarios":"tryLockFile calls syscall.Flock(fd, LOCK_EX|LOCK_NB) on the lock file and gets EWOULDBLOCK/EAGAIN (held by another process) or another flock error — the wrapper unfortunately labels both, but ErrHeld semantics target the busy case.","commonSituations":"Two CLI invocations racing on the same config/profile lock (e.g. concurrent `lark-cli auth login` in two terminals); a stale lock held by a crashed process whose fd lingers (rare for flock — dies with the process); NFS mounts where flock semantics are unreliable; a long-running command (update/install) still holding the lock.","solutions":["Check errors.Is(err, lockfile.ErrHeld) in the caller; if held, wait and retry with backoff rather than treating it as fatal corruption.","Find the competing process holding the lock (lsof on the lock file path from the message) and wait for it to finish.","Do not delete the lock file while another process may hold it — flock releases automatically when the holder exits.","On NFS, move the lock file to a local filesystem where flock is reliable."],"exampleFix":"// before\ncmd1: lark-cli config set a=b &\ncmd2: lark-cli config set c=d   // fails: lock held\n\n// after: serialize or retry\nif err := store.Update(...); errors.Is(err, lockfile.ErrHeld) {\n    time.Sleep(100 * time.Millisecond)\n    return store.Update(...) // retry\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":"func IsLockHeld(err error) bool {\n    return errors.Is(err, lockfile.ErrHeld)\n}","tryCatchPattern":"err := lf.TryLock()\nif IsLockHeld(err) {\n    // busy, not corrupt: back off and retry\n    time.Sleep(200 * time.Millisecond)\n    err = lf.TryLock()\n}\nif err != nil {\n    return err\n}\ndefer lf.Unlock()","preventionTips":["Check errors.Is(err, ErrHeld) instead of parsing message text.","Serialize CLI state-mutating commands in scripts; avoid parallel invocations.","Never delete lock files while a holder may exist — flock auto-releases on exit.","Keep lock files on local filesystems, not NFS."],"tags":["filesystem","locking","concurrency","unix"],"backgroundTag":"file-lock-held","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}