{"record":{"id":"a29839056da388aa","repo":"affaan-m/ECC","slug":"legacy-sync-path-changed-while-opening-filepath","errorCode":null,"errorMessage":"Legacy sync path changed while opening: ${filePath}","messagePattern":"Legacy sync path changed while opening: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/codex-legacy-sync.js","lineNumber":48,"sourceCode":"      } catch (lstatError) {\n        if (lstatError.code === 'ENOENT') return null;\n        throw lstatError;\n      }\n      throw error;\n    }\n    if (error.code === 'ELOOP') {\n      throw new Error(`Refusing to manage non-regular legacy sync path: ${filePath}`);\n    }\n    throw error;\n  }\n  const descriptorStat = fs.fstatSync(descriptor, { bigint: true });\n  let finalPathStat;\n  try {\n    finalPathStat = fs.lstatSync(filePath, { bigint: true });\n  } catch (error) {\n    fs.closeSync(descriptor);\n    if (error.code === 'ENOENT') {\n      throw new Error(`Legacy sync path changed while opening: ${filePath}`);\n    }\n    throw error;\n  }\n  if (\n    !descriptorStat.isFile()\n    || !finalPathStat.isFile()\n    || finalPathStat.isSymbolicLink()\n    || descriptorStat.dev !== finalPathStat.dev\n    || descriptorStat.ino !== finalPathStat.ino\n    || descriptorStat.nlink !== 1n\n    || finalPathStat.nlink !== 1n\n  ) {\n    fs.closeSync(descriptor);\n    throw new Error(`Refusing to manage non-regular legacy sync path: ${filePath}`);\n  }\n  return { descriptor, stat: fs.fstatSync(descriptor) };\n}\n","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/affaan-m/ECC/blob/06c5e118c4d3e6c3b7f9445f973a2194c82de193/scripts/lib/codex-legacy-sync.js#L30-L66","documentation":"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.","triggerScenarios":"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.","commonSituations":"Running `ecc codex sync` in parallel shells or CI jobs; editors with file watchers that recreate state files; overlapping hook executions.","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."],"exampleFix":"# before: two racing jobs\n$ ecc codex sync & ecc codex sync &\n# Error: Legacy sync path changed while opening\n\n# after: single writer\n$ ecc codex sync","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"function withRetry(fn, { attempts = 3, isTransient = (e) => /changed while opening/.test(e.message) } = {}) {\n  for (let i = 0; i < attempts; i++) {\n    try { return fn(); } catch (e) { if (i === attempts - 1 || !isTransient(e)) throw e; }\n  }\n}","preventionTips":["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."],"tags":["filesystem","race-condition","codex-sync"],"backgroundTag":"file-deleted-during-operation","analyzedSha":"06c5e118c4d3e6c3b7f9445f973a2194c82de193","analyzedAt":"2026-08-18T11:27:13.915Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}