{"record":{"id":"c04bb392613e3975","repo":"affaan-m/ECC","slug":"memory-destination-changed-while-it-was-being-crea","errorCode":null,"errorMessage":"Memory destination changed while it was being created.","messagePattern":"Memory destination changed while it was being created\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/memory-vault.js","lineNumber":215,"sourceCode":"  const flags = fs.constants.O_WRONLY\n    | fs.constants.O_CREAT\n    | fs.constants.O_EXCL\n    | (fs.constants.O_NOFOLLOW || 0);\n  let descriptor;\n  let operationError;\n  let cleanupError;\n  try {\n    descriptor = fs.openSync(temporaryPath, flags, 0o600);\n    const opened = fs.fstatSync(descriptor, { bigint: true });\n    const after = fs.lstatSync(temporaryPath, { bigint: true });\n    assertWithinTrustedRoot(temporaryPath, trustedRoot, 'write memory');\n    if (\n      !opened.isFile()\n      || after.isSymbolicLink()\n      || !after.isFile()\n      || !sameFileIdentity(after, opened)\n    ) {\n      throw new Error('Memory destination changed while it was being created.');\n    }\n    fs.writeFileSync(descriptor, content, 'utf8');\n    fs.fsyncSync(descriptor);\n    fs.closeSync(descriptor);\n    descriptor = undefined;\n    assertWithinTrustedRoot(filePath, trustedRoot, 'write memory');\n    fs.linkSync(temporaryPath, filePath);\n  } catch (error) {\n    operationError = error;\n  } finally {\n    if (descriptor !== undefined) {\n      try {\n        fs.closeSync(descriptor);\n      } catch (error) {\n        cleanupError = error;\n      }\n    }\n    try {","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/memory-vault.js#L197-L233","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nsaveMemory({ id: 'note-1', ... }); // concurrent worker also creating note-1 -> race\n// after\n// serialize writes per memory id\nawait withLock(`memory:note-1`, () => saveMemory({ id: 'note-1', ... }));","handlingStrategy":"retry","validationCode":"const st = fs.lstatSync(destPath);\nif (st.isSymbolicLink()) throw new Error('Destination is a symlink; aborting save.');","typeGuard":"const isSafeRegularFile = (p) => { try { const st = fs.lstatSync(p); return st.isFile() && !st.isSymbolicLink(); } catch { return true; } }; // true = path absent, safe to create","tryCatchPattern":"try {\n  saveMemory(memory);\n} catch (err) {\n  if (err.message.includes('changed while it was being created')) {\n    // transient race: wait briefly and retry once; investigate if persistent\n  } else throw err;\n}","preventionTips":["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."],"tags":["race-condition","filesystem","security","memory-vault"],"backgroundTag":"invalid-state-transition","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}