affaan-m/ECC · error · Error
Memory destination changed while it was being created.
Error message
Memory destination changed while it was being created.
What it means
writeCreateOnlyTextFile creates the destination with O_EXCL semantics and re-verifies, after opening, that the path still refers to the exact same regular file it opened (same identity, not a symlink, not replaced). If anything about the destination changed between stat and the post-open check, it throws this error to prevent writing through a swapped path (TOCTOU / symlink attack defense).
Solutions
- Re-run the save operation; transient races usually resolve on retry.
- Ensure no other process/tool is writing to or reorganizing the memory vault directory concurrently.
- Verify no symlink exists at the destination path; remove it and retry.
- If EEXIST-like races are common, serialize writes through a single process or use file locking.
Example fix
// before
saveMemory({ id: 'note-1', ... }); // concurrent worker also creating note-1 -> race
// after
// serialize writes per memory id
await withLock(`memory:note-1`, () => saveMemory({ id: 'note-1', ... })); Defensive patterns
Strategy: retry
Validate before calling
const st = fs.lstatSync(destPath);
if (st.isSymbolicLink()) throw new Error('Destination is a symlink; aborting save.'); Type guard
const isSafeRegularFile = (p) => { try { const st = fs.lstatSync(p); return st.isFile() && !st.isSymbolicLink(); } catch { return true; } }; // true = path absent, safe to create Try / catch
try {
saveMemory(memory);
} catch (err) {
if (err.message.includes('changed while it was being created')) {
// transient race: wait briefly and retry once; investigate if persistent
} else throw err;
} Prevention
- Run only one writer process against a vault at a time.
- Exclude the vault directory from sync tools (Dropbox/Drive) during writes.
- Never place symlinks inside the vault.
- Retry failed saves; races are usually transient.
When it happens
Trigger: saveMemory or ensureProjectScopeIgnored attempts to create a memory file and, between path creation and the identity check, the destination is replaced, deleted and recreated, or turned into a symbolic link by another process.
Common situations: Concurrent writers (two agents saving the same memory id simultaneously), build/sync tools touching the vault directory, or an attacker planting a symlink at the destination path.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Invalid ECC repo root: unreadable package.json at
- Refusing to create non-regular legacy sync path
- Refusing to read a file that changed during validation
- artifact byte count exceeded during reading
- artifact changed before reading
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/c04bb392613e3975.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/memory-vault.js:215
const flags = fs.constants.O_WRONLY
| fs.constants.O_CREAT
| fs.constants.O_EXCL
| (fs.constants.O_NOFOLLOW || 0);
let descriptor;
let operationError;
let cleanupError;
try {
descriptor = fs.openSync(temporaryPath, flags, 0o600);
const opened = fs.fstatSync(descriptor, { bigint: true });
const after = fs.lstatSync(temporaryPath, { bigint: true });
assertWithinTrustedRoot(temporaryPath, trustedRoot, 'write memory');
if (
!opened.isFile()
|| after.isSymbolicLink()
|| !after.isFile()
|| !sameFileIdentity(after, opened)
) {
throw new Error('Memory destination changed while it was being created.');
}
fs.writeFileSync(descriptor, content, 'utf8');
fs.fsyncSync(descriptor);
fs.closeSync(descriptor);
descriptor = undefined;
assertWithinTrustedRoot(filePath, trustedRoot, 'write memory');
fs.linkSync(temporaryPath, filePath);
} catch (error) {
operationError = error;
} finally {
if (descriptor !== undefined) {
try {
fs.closeSync(descriptor);
} catch (error) {
cleanupError = error;
}
}
try {View on GitHub (pinned to 8321021c54)