{"record":{"id":"1db0dfa79abae76b","repo":"larksuite/cli","slug":"lockfile-lock-already-held","errorCode":null,"errorMessage":"lockfile: lock already held","messagePattern":"lockfile: lock already held","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/lockfile/lockfile.go","lineNumber":21,"sourceCode":"\npackage lockfile\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\n\t\"github.com/larksuite/cli/internal/core\"\n\t\"github.com/larksuite/cli/internal/vfs\"\n)\n\n// safeIDChars strips path-traversal chars from app IDs.\nvar safeIDChars = regexp.MustCompile(`[^a-zA-Z0-9._-]`)\n\n// ErrHeld signals retryable contention; callers errors.Is to distinguish from real failures.\nvar ErrHeld = errors.New(\"lockfile: lock already held\")\n\ntype LockFile struct {\n\tpath string\n\tfile *os.File\n}\n\nfunc New(path string) *LockFile {\n\treturn &LockFile{path: path}\n}\n\n// ForSubscribe sanitises appID against path traversal before forming the lock filename.\nfunc ForSubscribe(appID string) (*LockFile, error) {\n\tif appID == \"\" {\n\t\treturn nil, fmt.Errorf(\"app ID must not be empty\")\n\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)","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/lockfile/lockfile.go#L3-L39","documentation":"Sentinel returned by TryLock when the advisory lockfile already exists and is held by another process; the doc comment marks it as retryable contention. It signals normal concurrency, not corruption - e.g. a second CLI invocation or an already-running event bus owns the lock.","triggerScenarios":"Thrown at internal/lockfile/lockfile.go:21 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Wait for the current holder to finish and retry the lock acquisition","Treat it as 'already running' and skip spawning a second instance, as busdiscover does for PID files"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"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"}