Skip to content

Commit aa49bdc

Browse files
committed
fix(registryLock): ensure file exists before locking to prevent corruption
Proper-lockfile requires the target file to exist. Added file creation logic and changed error handling to throw instead of degrade silently to prevent data corruption from concurrent access.
1 parent 69b5628 commit aa49bdc

File tree

1 file changed

+22
-8
lines changed

1 file changed

+22
-8
lines changed

src/agents/monitoring/registryLock.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,28 @@ export class RegistryLockService {
2222
* Acquire an exclusive lock on the registry file
2323
* Returns a release function to unlock the file
2424
*
25-
* If the file doesn't exist yet, creates a placeholder to lock against
25+
* Ensures the file exists before attempting to lock
2626
*/
2727
async acquireLock(): Promise<() => Promise<void>> {
2828
try {
29-
// For registry, we lock even if file doesn't exist yet
30-
// (prevents race during initial creation)
3129
const lockPath = this.registryPath;
3230

31+
// Ensure file exists before locking (proper-lockfile requires this)
32+
// Create empty file if it doesn't exist
33+
const { existsSync } = await import('fs');
34+
const { writeFile, mkdir } = await import('fs/promises');
35+
const { dirname } = await import('path');
36+
37+
if (!existsSync(lockPath)) {
38+
const dir = dirname(lockPath);
39+
if (!existsSync(dir)) {
40+
await mkdir(dir, { recursive: true });
41+
}
42+
// Create minimal valid registry file
43+
await writeFile(lockPath, JSON.stringify({ lastId: 0, agents: {} }, null, 2), 'utf-8');
44+
logger.debug(`Created registry file for locking: ${lockPath}`);
45+
}
46+
3347
// Acquire lock with proper-lockfile
3448
const release = await lockfile.lock(lockPath, {
3549
stale: 30000, // 30 second stale timeout to prevent deadlocks
@@ -38,7 +52,7 @@ export class RegistryLockService {
3852
minTimeout: 50,
3953
maxTimeout: 500,
4054
},
41-
realpath: false, // Don't resolve symlinks (registry might not exist yet)
55+
realpath: false, // Don't resolve symlinks
4256
lockfilePath: `${lockPath}.lock`, // Explicit lock file path
4357
});
4458

@@ -47,10 +61,10 @@ export class RegistryLockService {
4761

4862
return release;
4963
} catch (error) {
50-
logger.warn(`Failed to acquire lock for registry ${this.registryPath}: ${error}`);
51-
// Return no-op release function for graceful degradation
52-
// This allows the system to continue even if locking fails
53-
return async () => {};
64+
logger.error(`CRITICAL: Failed to acquire lock for registry ${this.registryPath}: ${error}`);
65+
// DO NOT silently degrade - throw error for critical operations
66+
// This prevents data corruption from unprotected concurrent access
67+
throw new Error(`Failed to acquire registry lock: ${error}`);
5468
}
5569
}
5670

0 commit comments

Comments
 (0)