affaan-m/ECC · error · Error

Refusing to replace modified legacy Codex artifact: ${filePa

Error message

Refusing to replace modified legacy Codex artifact: ${filePath}

What it means

While verifying prior install state, assertInstalledStateUnmodified() hits an entry whose installedSha256 is null — the path was recorded as absent (a tombstone for a file ECC intended to create) — but readRegularFileNoFollow() now finds a regular file there. Because ECC never wrote that file, overwriting it could destroy user data, so the reinstall is refused.

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 06c5e118c4)

Solutions

  1. Inspect the file named in the error; if it is disposable or yours, move it aside and re-run the sync
  2. If it must be kept, copy its content elsewhere, let ECC manage the path, then merge manually
  3. If the whole install state is stale, back up and delete the sync-state file to start fresh

Example fix

# before: retry throws Refusing to replace modified legacy Codex artifact: /home/u/.codex/config.toml
mv ~/.codex/config.toml ~/.codex/config.toml.pre-ecc
# after: re-run the legacy Codex sync, then merge your config back if needed
Defensive patterns

Strategy: validation

Validate before calling

const fs = require('fs'), path = require('path');
const conflicts = state.paths
  .filter(e => !e.installedSha256)
  .filter(e => fs.existsSync(path.resolve(e.path)));
if (conflicts.length) reportFileConflicts(conflicts); // resolve before re-running the sync

Try / catch

try { beginLegacySyncState({ codexHome }); }
catch (err) {
  if (/Refusing to replace modified legacy Codex artifact/.test(err.message)) {
    // the message contains the path: move the unexpected file aside, then retry
  } else throw err;
}

Prevention

When it happens

Trigger: A first sync recorded paths it planned to create and was interrupted before installing them; before the retry, someone (user, other tool, other agent) created a file at one of those paths, e.g. ~/.codex/config.toml now exists where the entry expected absence.

Common situations: User runs 'codex' login/config between a failed ECC install and the retry; dotfile managers or parallel agents writing into CODEX_HOME concurrently.

Related errors


AI-assisted analysis of affaan-m/ECC@06c5e118c4 (2026-08-18). Data as JSON: /api/errors/2a6e1cd684bd4bbe. Report an issue: GitHub.