affaan-m/ECC · error · Error

Refusing to manage non-regular legacy sync path

Error message

Refusing to manage non-regular legacy sync path: ${filePath}

What it means

openRegularFileNoFollow safely opens legacy Codex sync files, refusing anything that is not a plain regular file. When an open fails with ENOENT, it lstats the path: if the path actually resolves to a symlink or non-regular file (a race where it vanished/reappeared, or the open failed for another reason), it throws this error instead of proceeding. It protects against managing or overwriting links and special files.

Solutions

  1. Replace the symlink/special file at the path with a real regular file (cp --remove-destination the target content to a plain file).
  2. Re-run the sync/uninstall command after the path is a regular file.
  3. If you intentionally symlink dotfiles, exclude that path from legacy Codex sync or manage it yourself outside this tool.
  4. Inspect the path: ls -la ~/.codex/... to confirm what is there before retrying.

Example fix

// before
ln -s ~/dotfiles/codex-config.toml ~/.codex/config.toml   # sync refuses
// after
rm ~/.codex/config.toml && cp ~/dotfiles/codex-config.toml ~/.codex/config.toml
Defensive patterns

Strategy: validation

Validate before calling

const fs = require('fs');
const st = fs.lstatSync(targetPath); // throws ENOENT if absent (fine)
if (st.isSymbolicLink() || !st.isFile()) throw new Error(`Path must be a regular file: ${targetPath}`);

Type guard

function isRegularFileSync(p) { try { const st = require('fs').lstatSync(p); return st.isFile() && !st.isSymbolicLink(); } catch { return false; } }

Try / catch

try { await uninstallLegacyCodexSync(); } catch (e) { if (e.message.startsWith('Refusing to manage non-regular')) { console.error('Replace the symlink/special file with a plain file, then retry'); } else throw e; }

Prevention

When it happens

Trigger: Running install/rollback/uninstall legacy Codex sync when the target path (e.g. ~/.codex config file) is a symlink to elsewhere, a FIFO/device/directory, or turns into one between the failed open and the lstat check (TOCTOU).

Common situations: Users symlink their dotfiles (e.g. ~/.codex/config.toml -> ~/dotfiles/config.toml) into a dotfiles repo; container images placing sockets or special files at the path; partially deleted sync state.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/5b5106894a403620. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/codex-legacy-sync.js:28

const BEGIN_MARKER = '<!-- BEGIN ECC -->';
const END_MARKER = '<!-- END ECC -->';

function getStatePath(codexHome) {
  return path.join(codexHome, 'ecc', 'legacy-sync-state.json');
}

function openRegularFileNoFollow(filePath, writable = false) {
  const noFollow = fs.constants.O_NOFOLLOW || 0;
  const flags = (writable ? fs.constants.O_RDWR : fs.constants.O_RDONLY) | noFollow;
  let descriptor;
  try {
    descriptor = fs.openSync(filePath, flags);
  } catch (error) {
    if (error.code === 'ENOENT') {
      try {
        const unresolved = fs.lstatSync(filePath);
        if (unresolved.isSymbolicLink() || !unresolved.isFile()) {
          throw new Error(`Refusing to manage non-regular legacy sync path: ${filePath}`);
        }
      } catch (lstatError) {
        if (lstatError.code === 'ENOENT') return null;
        throw lstatError;
      }
      throw error;
    }
    if (error.code === 'ELOOP') {
      throw new Error(`Refusing to manage non-regular legacy sync path: ${filePath}`);
    }
    throw error;
  }
  const descriptorStat = fs.fstatSync(descriptor, { bigint: true });
  let finalPathStat;
  try {
    finalPathStat = fs.lstatSync(filePath, { bigint: true });
  } catch (error) {
    fs.closeSync(descriptor);

View on GitHub (pinned to 8321021c54)