{"record":{"id":"204e6fc2cc06921a","repo":"larksuite/cli","slug":"open-lock-file-w","errorCode":null,"errorMessage":"open lock file: %w","messagePattern":"open lock file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/lockfile/lockfile.go","lineNumber":54,"sourceCode":"\t}\n\tdir := filepath.Join(core.GetConfigDir(), \"locks\")\n\tif err := vfs.MkdirAll(dir, 0700); err != nil {\n\t\treturn nil, fmt.Errorf(\"create lock dir: %w\", err)\n\t}\n\tsafe := safeIDChars.ReplaceAllString(appID, \"_\")\n\tname := filepath.Base(fmt.Sprintf(\"subscribe_%s.lock\", safe))\n\tpath := filepath.Join(dir, name)\n\treturn New(path), nil\n}\n\n// TryLock acquires an exclusive non-blocking lock; auto-released on process exit.\nfunc (l *LockFile) TryLock() error {\n\tif l.file != nil {\n\t\treturn fmt.Errorf(\"%w: %s\", ErrHeld, l.path)\n\t}\n\tf, err := vfs.OpenFile(l.path, os.O_CREATE|os.O_RDWR, 0600)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open lock file: %w\", err)\n\t}\n\tif err := tryLockFile(f); err != nil {\n\t\tf.Close()\n\t\treturn err\n\t}\n\tl.file = f\n\treturn nil\n}\n\n// Unlock keeps the file on disk to avoid inode-reuse races between unlock and competing open+flock.\nfunc (l *LockFile) Unlock() error {\n\tif l.file == nil {\n\t\treturn nil\n\t}\n\terr := unlockFile(l.file)\n\tcloseErr := l.file.Close()\n\tl.file = nil\n\tif err != nil {","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/lockfile/lockfile.go#L36-L72","documentation":"TryLock opens (creating if necessary, mode 0600) the lock file through vfs.OpenFile before locking; failure here is wrapped as \"open lock file: %w\" with the underlying cause. This is an environment failure, not lock contention — the file itself could not be created or opened.","triggerScenarios":"vfs.OpenFile fails inside TryLock: the parent lock directory was deleted after ForSubscribe, permissions deny write, path invalid, disk full, or too many open files.","commonSituations":"Another tool wiped the locks directory between ForSubscribe and TryLock; read-only filesystem; ulimit -n exhausted in long-running agents; config dir remounted read-only.","solutions":["Inspect the wrapped cause for the concrete OS error (ENOENT/EACCES/EMFILE...).","Recreate the lock directory (re-run the ForSubscribe path or mkdir with 0700).","Fix filesystem permissions or free file descriptors (raise ulimit, close leaks).","Ensure the path from Path() is valid for the current host (FileIO/vfs scoping)."],"exampleFix":"// before\nf, _ := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0600) // dir missing\n// after\nos.MkdirAll(filepath.Dir(path), 0o700)\nf, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o600)\nif err != nil { return fmt.Errorf(\"open lock file: %w\", err) }","handlingStrategy":"try-catch","validationCode":"if _, err := os.Stat(filepath.Dir(lf.Path())); err != nil {\n    return fmt.Errorf(\"lock dir missing: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := lf.TryLock(); err != nil {\n    if !errors.Is(err, lockfile.ErrHeld) {\n        return fmt.Errorf(\"lock file unavailable: %w\", err) // env problem\n    }\n}","preventionTips":["Don't delete the locks directory while sessions run","Keep ulimit -n comfortably above open handles in long-lived agents","Run on a writable filesystem; avoid read-only remounts for the config dir"],"tags":["filesystem","io","permissions"],"backgroundTag":"filesystem-permission-denied","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"}