eyaltoledano/claude-task-master · error
Failed to acquire lock on ${filepath} after ${maxRetries} at
Error message
Failed to acquire lock on ${filepath} after ${maxRetries} attempts What it means
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.
Source
Thrown at scripts/modules/utils.js:161
if (statErr.code === 'ENOENT') {
continue;
}
throw statErr;
}
// Lock exists and isn't stale (or we couldn't handle it), wait and retry
if (attempt < maxRetries - 1) {
const waitMs = retryDelay * Math.pow(2, attempt);
await sleep(waitMs);
}
} else {
throw err;
}
}
}
if (!acquired) {
throw new Error(
`Failed to acquire lock on ${filepath} after ${maxRetries} attempts`
);
}
try {
return await callback();
} finally {
// Release lock
try {
await fsPromises.unlink(lockPath);
} catch (releaseError) {
// Always log lock release failures - they indicate potential issues
log(
'warn',
`Failed to release lock for ${filepath}: ${releaseError.message}`
);
}
}View on GitHub (pinned to c0c98d367c)
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
Example fix
// before
await withFileLock(path, { maxRetries: 3 }, fn);
// after
await withFileLock(path, { maxRetries: 10, retryDelay: 200 }, fn); // after clearing stale .lock Defensive patterns
Strategy: retry
Validate before calling
import fs from 'fs';
const lockPath = `${filepath}.lock`;
if (fs.existsSync(lockPath)) console.warn('Lock exists; ensure no other process is running before proceeding'); Try / catch
let result;
try {
result = await withFileLock(filepath, { maxRetries: 10 }, callback);
} catch (e) {
if (e.message.startsWith('Failed to acquire lock')) {
fs.rmSync(`${filepath}.lock`, { force: true }); // only if no other process is running
result = await withFileLock(filepath, { maxRetries: 10 }, callback);
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/340871880a692ce6.
Report an issue: GitHub.