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

  1. Ensure no other task-master process is running, then retry
  2. Remove the stale lock file (e.g. <file>.lock in the same directory) if no process holds it
  3. Increase maxRetries/retry delay when invoking withFileLock
  4. 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

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.