{"record":{"id":"340871880a692ce6","repo":"eyaltoledano/claude-task-master","slug":"failed-to-acquire-lock-on-filepath-after-maxr","errorCode":null,"errorMessage":"Failed to acquire lock on ${filepath} after ${maxRetries} attempts","messagePattern":"Failed to acquire lock on (.+?) after (.+?) attempts","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/modules/utils.js","lineNumber":161,"sourceCode":"\t\t\t\t\tif (statErr.code === 'ENOENT') {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthrow statErr;\n\t\t\t\t}\n\n\t\t\t\t// Lock exists and isn't stale (or we couldn't handle it), wait and retry\n\t\t\t\tif (attempt < maxRetries - 1) {\n\t\t\t\t\tconst waitMs = retryDelay * Math.pow(2, attempt);\n\t\t\t\t\tawait sleep(waitMs);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!acquired) {\n\t\tthrow new Error(\n\t\t\t`Failed to acquire lock on ${filepath} after ${maxRetries} attempts`\n\t\t);\n\t}\n\n\ttry {\n\t\treturn await callback();\n\t} finally {\n\t\t// Release lock\n\t\ttry {\n\t\t\tawait fsPromises.unlink(lockPath);\n\t\t} catch (releaseError) {\n\t\t\t// Always log lock release failures - they indicate potential issues\n\t\t\tlog(\n\t\t\t\t'warn',\n\t\t\t\t`Failed to release lock for ${filepath}: ${releaseError.message}`\n\t\t\t);\n\t\t}\n\t}","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/scripts/modules/utils.js#L143-L179","documentation":"withFileLock implements a retrying lock acquisition (lockfile-based) before running an async callback. If the lock cannot be acquired after maxRetries attempts, it throws this error instead of risking concurrent writes to the file.","triggerScenarios":"Another process (or a crashed prior run) holds the lock file for the target file longer than the total retry window; all retry attempts fail to create/claim the lock.","commonSituations":"Two task-master CLI/MCP instances running simultaneously, a stale lock file left by a killed process, network filesystems with poor atomic-rename semantics, or extremely short maxRetries configuration.","solutions":["Ensure no other task-master process is running, then retry","Remove the stale lock file (e.g. <file>.lock in the same directory) if no process holds it","Increase maxRetries/retry delay when invoking withFileLock","Avoid running task-master on network mounts without proper locking support"],"exampleFix":"// before\nawait withFileLock(path, { maxRetries: 3 }, fn);\n// after\nawait withFileLock(path, { maxRetries: 10, retryDelay: 200 }, fn); // after clearing stale .lock","handlingStrategy":"retry","validationCode":"import fs from 'fs';\nconst lockPath = `${filepath}.lock`;\nif (fs.existsSync(lockPath)) console.warn('Lock exists; ensure no other process is running before proceeding');","typeGuard":null,"tryCatchPattern":"let result;\ntry {\n  result = await withFileLock(filepath, { maxRetries: 10 }, callback);\n} catch (e) {\n  if (e.message.startsWith('Failed to acquire lock')) {\n    fs.rmSync(`${filepath}.lock`, { force: true }); // only if no other process is running\n    result = await withFileLock(filepath, { maxRetries: 10 }, callback);\n  } else throw e;\n}","preventionTips":["Run only one task-master process at a time","Clean stale lock files after crashed runs","Increase maxRetries/retryDelay on slow filesystems","Avoid lock-sensitive operations on NFS/network drives"],"tags":["concurrency","file-lock","resource-contention"],"backgroundTag":"file-lock-acquisition-failed","analyzedSha":"c0c98d367c55296bfe69e65680625b6db437af02","analyzedAt":"2026-08-29T02:56:26.071Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}