affaan-m/ECC · error · Error
Refusing to replace modified legacy Codex artifact
Error message
Refusing to replace modified legacy Codex artifact: ${filePath} What it means
assertInstalledStateUnmodified refuses to proceed when an entry in the sync state has NO installedSha256 (i.e. the artifact was recorded as previously absent) but the file now EXISTS on disk. Since the installer cannot verify the on-disk content matches what it wrote (there is no baseline digest), any existing file could be user-modified, so it aborts rather than overwriting user data.
Solutions
- Compare the on-disk file with the intended artifact; if the content is yours and unwanted, delete or move it, then rerun the install.
- Regenerate the sync state (delete statePath and reinstall) so the entry reflects the file's current hash instead of the 'absent' baseline.
- If the file is legitimately the artifact (e.g. restored from the plugin), verify its sha256 matches the shipped artifact and update the state entry's installedSha256.
- Back up the existing file before any deletion so user customizations are not lost.
Example fix
// before: file exists but state has no installedSha256 mv ~/.codex/AGENTS.md ~/.codex/AGENTS.md.user-backup // after: path is clear; rerun install, then merge any custom edits back manually
Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs';
function artifactAbsentEntryConflict(state, filePath) {
const entry = state.paths.find(e => path.resolve(e.path) === path.resolve(filePath));
return entry && !entry.installedSha256 && fs.existsSync(filePath);
} Try / catch
try {
beginLegacySyncState(options);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Refusing to replace modified legacy Codex artifact')) {
const p = err.message.split('artifact: ')[1];
console.error(`Back up and remove your local copy of ${p}, then rerun install`);
process.exitCode = 1;
} else {
throw err;
}
} Prevention
- Do not restore managed files from backups after uninstalling; reinstall instead.
- Use the plugin's uninstall flow so state entries stay consistent with what is on disk.
- Keep other dotfile/sync tools from writing into codexHome-managed paths.
- Back up any unexpected file before deleting it to clear the conflict.
When it happens
Trigger: beginLegacySyncState -> assertInstalledStateUnmodified over a state entry without installedSha256 while readRegularFileNoFollow(filePath) returns a snapshot — i.e. the file was deleted at install time (recorded as absent) and has since been created by anything else (user, another tool, a later partial install).
Common situations: User restored a managed file from a backup after uninstalling; another plugin or dotfiles manager recreated the file; a failed previous install left the file behind without updating state; git checkout/branch switch restored the file.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- Invalid legacy Codex sync state at
- Legacy Codex sync state not found at
- anchor evidence source duration is not bound to its receipt…
- artifact has invalid reference SHA-256
- artifact SHA-256 does not match receipt
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/2a6e1cd684bd4bbe.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/codex-legacy-sync.js:212
return {
path: filePath,
installedSha256: null,
previousType,
previousContentBase64: snapshot ? snapshot.content.toString('base64') : null,
previousMode: snapshot ? snapshot.mode : null,
};
}
function assertInstalledStateUnmodified(state) {
for (const entry of state.paths) {
const filePath = path.resolve(entry.path);
const trustedRoot = getTrustedRoot(state, filePath);
if (!trustedRoot || hasUnsafeManagedAncestor(filePath, trustedRoot)) {
throw new Error(`Refusing to reuse unsafe legacy Codex ownership path: ${filePath}`);
}
const snapshot = readRegularFileNoFollow(filePath);
if (!entry.installedSha256) {
if (snapshot) throw new Error(`Refusing to replace modified legacy Codex artifact: ${filePath}`);
continue;
}
const digest = snapshot
? crypto.createHash('sha256').update(snapshot.content).digest('hex')
: null;
if (digest !== entry.installedSha256) {
throw new Error(`Refusing to replace modified legacy Codex artifact: ${filePath}`);
}
}
}
function beginLegacySyncState(options) {
const codexHome = path.resolve(options.codexHome);
const statePath = getStatePath(codexHome);
const configPath = path.join(codexHome, 'config.toml');
const agentsPath = path.join(codexHome, 'AGENTS.md');
const installedHooksPath = options.installedHooksPath ? path.resolve(options.installedHooksPath) : null;
const priorState = readStateIfPresent(statePath);View on GitHub (pinned to 8321021c54)