EveryInc/compound-engineering-plugin · error · Error
${collectionPath} changed since it was inspected; refusing t
Error message
${collectionPath} changed since it was inspected; refusing to remove it. The entry taken for validation is preserved at ${recoveryPath}; the current entry at ${collectionPath} was not overwritten (${reason}) What it means
If restore-after-validation fails inside removeManagedCollectionLink(), restorationFailed() re-raises a combined error: the path changed since inspection, the validated entry is preserved at recoveryPath, and the current entry at collectionPath was left untouched. It distinguishes this late-failure case from the early changed() abort so the caller knows exactly which copy lives where.
Source
Thrown at src/dev/codex-dev.ts:336
`The entry is preserved at ${recoveryPath} (${reason})`,
)
}
const stat = await fs.lstat(recoveryPath)
let actualTarget: string | undefined
if (stat.isSymbolicLink()) {
actualTarget = await fs.readlink(recoveryPath)
}
if (stat.isSymbolicLink() && actualTarget === expectedTarget) {
await fs.unlink(recoveryPath)
await fs.rmdir(recoveryDir)
return true
}
const restorationFailed = (error: unknown): never => {
const reason = error instanceof Error ? error.message : String(error)
throw new Error(
`${collectionPath} changed since it was inspected; refusing to remove it. ` +
`The entry taken for validation is preserved at ${recoveryPath}; ` +
`the current entry at ${collectionPath} was not overwritten (${reason})`,
)
}
if (stat.isSymbolicLink()) {
try {
await fs.symlink(actualTarget!, collectionPath)
} catch (error) {
restorationFailed(error)
}
await fs.unlink(recoveryPath)
await fs.rmdir(recoveryDir)
return changed()
}
if (stat.isFile()) {View on GitHub (pinned to c9c10f8c75)
Solutions
- Locate the intact original at recoveryPath from the error message
- Check what is now at collectionPath (`ls -la <collectionPath>`) and decide which copy to keep
- Stop concurrent codex:dev / sync processes, then re-run the removal or manually place the recovery copy
- Re-run `bun run codex:dev -- status` to confirm final state
Example fix
# before (race) $ bun run codex:dev -- remote # another terminal: rm -rf CODEX_HOME/skills/... mid-run # Error: ... changed since it was inspected; ... preserved at <recoveryPath> # after $ # close other sessions/sync tools, restore from recoveryPath if needed $ mv "$recoveryPath" "$collectionPath" $ bun run codex:dev -- remote
Defensive patterns
Strategy: try-catch
Validate before calling
// verify nothing else mutates the path during removal
const watcher = fs.watch(path.dirname(collectionPath));
watcher.on("change", () => console.warn("path changing during removal")); Try / catch
try {
await removeLocalCollection(context);
} catch (e) {
if (String(e).includes("changed since it was inspected")) {
const m = String(e).match(/preserved at (\S+)/);
if (m) await fs.rename(m[1], context.collectionPath); // put the taken entry back
} else throw e;
} Prevention
- Serialize all codex:dev mutations (no concurrent local/remote switches)
- Exclude CODEX_HOME from real-time sync/antivirus scanning during operations
- Treat the recoveryPath in the message as the authoritative copy of the original entry
When it happens
Trigger: During the restore phase of removal, restoring the taken entry to collectionPath fails (EEXIST, EACCES, ENOENT) because something appeared or changed at collectionPath after it was inspected and taken.
Common situations: Another process recreated the skills path while the removal was mid-flight (TOCTOU race); antivirus or file-sync software recreating the directory; the user editing the path during a long-running command.
Related errors
- ${collectionPath} changed since it was inspected; refusing t
- ${context.collectionPath} is a broken symlink; refusing to o
- Could not validate ${collectionPath} after taking it. The en
- ${context.collectionPath} exists and is not a symlink; refus
- ${context.collectionPath} points outside a Compound Engineer
AI-assisted analysis of EveryInc/compound-engineering-plugin@c9c10f8c75 (2026-08-31).
Data as JSON: /api/errors/5adcabdb5c9ef70a.
Report an issue: GitHub.