{"record":{"id":"4cec4cd43bc7c801","repo":"larksuite/cli","slug":"w-lock-s-syscall-v-4cec4c","errorCode":null,"errorMessage":"%w (lock: %s, syscall: %v)","messagePattern":"%w \\(lock: (.+?), syscall: (.+?)\\)","errorType":"exception","errorClass":"ErrHeld","httpStatus":null,"severity":"error","filePath":"internal/lockfile/lock_windows.go","lineNumber":37,"sourceCode":")\n\nconst (\n\tlockfileExclusiveLock   = 0x00000002\n\tlockfileFailImmediately = 0x00000001\n)\n\nfunc tryLockFile(f *os.File) error {\n\tvar ol syscall.Overlapped\n\thandle := syscall.Handle(f.Fd())\n\tr1, _, err := procLockFileEx.Call(\n\t\tuintptr(handle),\n\t\tuintptr(lockfileExclusiveLock|lockfileFailImmediately),\n\t\t0,\n\t\t1, 0,\n\t\tuintptr(unsafe.Pointer(&ol)),\n\t)\n\tif r1 == 0 {\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\tvar ol syscall.Overlapped\n\thandle := syscall.Handle(f.Fd())\n\tr1, _, err := procUnlockFile.Call(\n\t\tuintptr(handle),\n\t\t0,\n\t\t1, 0,\n\t\tuintptr(unsafe.Pointer(&ol)),\n\t)\n\tif r1 == 0 {\n\t\treturn err\n\t}\n\treturn nil\n}","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/lockfile/lock_windows.go#L19-L55","documentation":"On Windows, tryLockFile calls LockFileEx with LK_LOCK_EXCLUSIVE|LK_FAIL_IMMEDIATELY semantics; if the syscall returns 0 the region is already locked (or the syscall failed). The error wraps the sentinel ErrHeld so callers can errors.Is it as retryable contention, and it embeds the lock file name and the underlying syscall error for diagnosis.","triggerScenarios":"Calling LockFile.TryLock() on Windows when another process (or a stale handle) already holds the OS-level byte-range lock on the lock file, or when LockFileEx itself fails (e.g. invalid handle, low resources).","commonSituations":"Two CLI event-subscribe sessions running concurrently for the same app ID on the same machine; a previous subscribe process that crashed without releasing while its handle lingers; antivirus or backup tools briefly holding the file.","solutions":["Check errors.Is(err, lockfile.ErrHeld) and treat it as contention: inform the user another subscribe is active or retry after it exits.","Read the embedded syscall error (%v) — if it is not ERROR_LOCK_VIOLATION, investigate handle/filesystem issues instead of retrying.","Ensure the competing process exits cleanly so the OS auto-releases the lock (locks auto-release on process death).","Run the command with a different app ID/config dir if you intentionally need parallel sessions."],"exampleFix":"// before\nif err := lf.TryLock(); err != nil {\n    return err\n}\n// after\nif err := lf.TryLock(); err != nil {\n    if errors.Is(err, lockfile.ErrHeld) {\n        return fmt.Errorf(\"another subscribe is running for this app; try again later\")\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"lf, err := lockfile.ForSubscribe(appID)\nif err != nil { return err }\nif err := lf.TryLock(); err != nil {\n    if errors.Is(err, lockfile.ErrHeld) {\n        return fmt.Errorf(\"another subscribe process holds the lock (%s)\", lf.Path())\n    }\n    return err\n}\ndefer lf.Unlock()","preventionTips":["Always branch on errors.Is(err, lockfile.ErrHeld) for contention vs real failures","Ensure subscribe processes exit cleanly so OS locks auto-release","Avoid running parallel subscribe sessions for the same app ID on one host"],"tags":["windows","file-locking","concurrency"],"backgroundTag":"file-lock-contention","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"}