affaan-m/ECC · error · Error
Legacy sync path changed while opening: ${filePath}
Error message
Legacy sync path changed while opening: ${filePath} What it means
The state file was opened successfully with O_NOFOLLOW, but the subsequent lstat on the path returned ENOENT — the directory entry vanished between the open() and the lstat(). Because the module verifies the opened descriptor against the path (dev/ino identity) to prevent time-of-check/time-of-use substitution, an entry that disappears mid-flight cannot be verified, so it aborts with a descriptor-closing cleanup.
Source
Thrown at scripts/lib/codex-legacy-sync.js:48
} catch (lstatError) {
if (lstatError.code === 'ENOENT') return null;
throw lstatError;
}
throw error;
}
if (error.code === 'ELOOP') {
throw new Error(`Refusing to manage non-regular legacy sync path: ${filePath}`);
}
throw error;
}
const descriptorStat = fs.fstatSync(descriptor, { bigint: true });
let finalPathStat;
try {
finalPathStat = fs.lstatSync(filePath, { bigint: true });
} catch (error) {
fs.closeSync(descriptor);
if (error.code === 'ENOENT') {
throw new Error(`Legacy sync path changed while opening: ${filePath}`);
}
throw error;
}
if (
!descriptorStat.isFile()
|| !finalPathStat.isFile()
|| finalPathStat.isSymbolicLink()
|| descriptorStat.dev !== finalPathStat.dev
|| descriptorStat.ino !== finalPathStat.ino
|| descriptorStat.nlink !== 1n
|| finalPathStat.nlink !== 1n
) {
fs.closeSync(descriptor);
throw new Error(`Refusing to manage non-regular legacy sync path: ${filePath}`);
}
return { descriptor, stat: fs.fstatSync(descriptor) };
}
View on GitHub (pinned to 06c5e118c4)
Solutions
- Re-run the command — this is a transient race and normally succeeds when only one writer is active.
- Serialize sync operations (lock file, single CI job, or sequential shell steps) so only one process manages the state file.
- Exclude the state directory from antivirus/backup real-time scanning that may unlink and recreate files.
Example fix
# before: two racing jobs $ ecc codex sync & ecc codex sync & # Error: Legacy sync path changed while opening # after: single writer $ ecc codex sync
Defensive patterns
Strategy: retry
Try / catch
function withRetry(fn, { attempts = 3, isTransient = (e) => /changed while opening/.test(e.message) } = {}) {
for (let i = 0; i < attempts; i++) {
try { return fn(); } catch (e) { if (i === attempts - 1 || !isTransient(e)) throw e; }
}
} Prevention
- Run only one sync operation at a time — use a lockfile (e.g. proper-lockfile) around state-file management.
- Schedule CI jobs touching the same state file at non-overlapping times.
When it happens
Trigger: Two sync operations racing on the same state file (one removes/renames it while another opens it); aggressive antivirus/backup/cleanup tools unlinking the file at that moment.
Common situations: Running `ecc codex sync` in parallel shells or CI jobs; editors with file watchers that recreate state files; overlapping hook executions.
Related errors
- Refusing to create non-regular legacy sync path: ${filePath}
- Legacy sync path changed before removal; preserved replaceme
- Legacy sync path changed before removal: ${filePath}
- Refusing to manage non-regular legacy sync path: ${filePath}
- Legacy Codex sync state not found at ${statePath}
AI-assisted analysis of affaan-m/ECC@06c5e118c4 (2026-08-18).
Data as JSON: /api/errors/a29839056da388aa.
Report an issue: GitHub.