{"record":{"id":"e85140edd5923bae","repo":"affaan-m/ECC","slug":"legacy-sync-path-changed-before-removal-preserved","errorCode":null,"errorMessage":"Legacy sync path changed before removal; preserved replacement at ${quarantinePath}","messagePattern":"Legacy sync path changed before removal; preserved replacement at (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/codex-legacy-sync.js","lineNumber":121,"sourceCode":"}\n\nfunction removeOpenedRegularFile(filePath, opened) {\n  const quarantineDir = fs.mkdtempSync(path.join(path.dirname(filePath), '.ecc-remove-'));\n  const quarantinePath = path.join(quarantineDir, path.basename(filePath));\n  fs.renameSync(filePath, quarantinePath);\n  const quarantined = openRegularFileNoFollow(quarantinePath);\n  const openedStat = fs.fstatSync(opened.descriptor, { bigint: true });\n  const quarantinedStat = fs.fstatSync(quarantined.descriptor, { bigint: true });\n  fs.closeSync(quarantined.descriptor);\n  fs.closeSync(opened.descriptor);\n  opened.descriptor = null;\n  if (quarantinedStat.dev !== openedStat.dev || quarantinedStat.ino !== openedStat.ino) {\n    try {\n      fs.linkSync(quarantinePath, filePath);\n      fs.unlinkSync(quarantinePath);\n      fs.rmdirSync(quarantineDir);\n    } catch (_restoreError) {\n      throw new Error(\n        `Legacy sync path changed before removal; preserved replacement at ${quarantinePath}`\n      );\n    }\n    throw new Error(`Legacy sync path changed before removal: ${filePath}`);\n  }\n  fs.unlinkSync(quarantinePath);\n  fs.rmdirSync(quarantineDir);\n}\n\nfunction atomicWriteJson(filePath, value) {\n  fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });\n  const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;\n  fs.writeFileSync(tempPath, `${JSON.stringify(value, null, 2)}\\n`, { mode: 0o600 });\n  fs.renameSync(tempPath, filePath);\n}\n\nfunction readState(statePath) {\n  const snapshot = readRegularFileNoFollow(statePath, 'utf8');","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/codex-legacy-sync.js#L103-L139","documentation":"removeOpenedRegularFile quarantines the opened file (renaming it away) and then verifies the quarantined node still matches the originally opened descriptor's dev/ino before deleting it. If they differ, something replaced the path between open and quarantine; the code attempts to restore the quarantined file to its original path, and if even that restore fails it throws this error while deliberately preserving the replacement file at quarantinePath so nothing is lost.","triggerScenarios":"The path was swapped (replaced by another file) between openRegularFileNoFollow and the quarantine rename during rollbackLegacyCodexSync/uninstallLegacyCodexSync, AND the restore attempt (linkSync/unlinkSync/rmdirSync of the quarantine) itself failed — e.g. the original path reappeared, permissions changed, or the quarantine dir was disturbed concurrently.","commonSituations":"A concurrent installer or file watcher recreates the sync file during uninstall; another instance of the script is mid-operation on the same path; restore fails because the original path got recreated by the racing process.","solutions":["Inspect the preserved replacement file at the quarantinePath given in the message and decide whether to keep or delete it manually","Re-run the uninstall/rollback when nothing else touches the path (close editors, watchers, other terminals)","Remove the re-created file at the original path first, then re-run so restore can succeed","Serialize script runs with a lock to prevent two concurrent sync operations"],"exampleFix":"# before: blind retry\nnpm run <uninstall-command>\n# after: inspect preserved copy, clean up, then retry\ncat <quarantineDir>/<quarantined-file>   # review preserved replacement\nrm -rf <quarantineDir>                    # once reviewed\nnpm run <uninstall-command>","handlingStrategy":"try-catch","validationCode":"// ensure no concurrent writer holds the path before calling\nimport fs from 'node:fs';\nconst before = fs.statSync(p);\nawait new Promise(r => setTimeout(r, 50));\nconst after = fs.statSync(p);\nif (before.ino !== after.ino || before.dev !== after.dev) throw new Error('path is being rewritten by another process');","typeGuard":null,"tryCatchPattern":"try {\n  await uninstallLegacyCodexSync(p);\n} catch (err) {\n  if (err.message.includes('preserved replacement at')) {\n    const quarantinePath = err.message.match(/preserved replacement at (\\S+)/)?.[1];\n    // review and manually resolve the preserved copy before retrying\n  } else throw err;\n}","preventionTips":["Stop editors/watchers that atomically rewrite files in the sync directory during uninstall","Serialize all script instances with a lockfile","Always read the quarantinePath from the error message before deleting quarantine dirs","Run uninstall/rollback in a quiet window with no parallel npm operations"],"tags":["filesystem","race-condition","toctou","data-safety"],"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"}