affaan-m/ECC · error · Error
Refusing to record a legacy sync path outside trusted roots
Error message
Refusing to record a legacy sync path outside trusted roots: ${filePath} What it means
recordLegacySyncPath refuses to register a file path in the legacy sync state unless that path resolves under one of the trusted roots recorded in the sync state. This is a safety guard so the tool never records or later mutates files outside directories it has been explicitly allowed to manage.
Solutions
- Check that the filePath you pass is inside the Codex home or another trusted root recorded in the state file
- Add the parent directory to trustedRoots by starting sync from that root (beginLegacySyncState accumulates trusted roots)
- Resolve symlinks in your path (fs.realpathSync) before calling, so it lands inside a trusted root
- Verify the state file's trustedRoots array includes the intended directory
Example fix
// before
recordLegacySyncPath({ statePath, filePath: '/some/other/place/config.toml' })
// after
const real = require('fs').realpathSync('/some/other/place/config.toml');
// ensure 'real' is under codexHome or a trusted root, then:
recordLegacySyncPath({ statePath, filePath: real }); Defensive patterns
Strategy: validation
Validate before calling
const filePath = path.resolve(candidate);
const state = readState(statePath);
if (!getTrustedRoot(state, filePath)) throw new Error('path not under trusted roots: ' + filePath); Type guard
const isUnderRoot = (p, root) => path.resolve(p).startsWith(path.resolve(root) + path.sep);
Try / catch
try { recordLegacySyncPath(opts); } catch (e) {
if (String(e.message).includes('outside trusted roots')) console.error('Add', path.dirname(opts.filePath), 'to trusted roots first');
else throw e;
} Prevention
- Record paths that live inside codexHome or another trusted root
- Resolve symlinks with fs.realpathSync before recording
- Seed trustedRoots via beginLegacySyncState for every extra directory you manage
- Never record ad-hoc temp paths
When it happens
Trigger: Calling recordLegacySyncPath({ statePath, filePath }) where path.resolve(filePath) is not inside any trusted root returned by getTrustedRoot(state, filePath) — trusted roots are seeded from codexHome plus prior state's trustedRoots/installedHooksPath.
Common situations: Passing an absolute path in a different directory tree than the Codex home; a path whose symlink resolution escapes the trusted root; passing a repo file path while only codexHome is trusted.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- artifact path must be canonical and absolute
- Path ' ' targets a system directory
- Refusing to manage legacy sync path through symlinked…
- Unsafe state-store path
- artifact must be a resident regular file
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/4081be85c9d64612.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/codex-legacy-sync.js:279
paths: [],
rollbackPaths: [],
};
for (const [key, filePath] of [['config', configPath], ['agents', agentsPath]]) {
if (priorState) break;
const snapshot = readRegularFileNoFollow(filePath, 'utf8');
state.before[key] = snapshot ? snapshot.content : null;
}
atomicWriteJson(statePath, state);
return statePath;
}
function recordLegacySyncPath(options) {
const state = readState(options.statePath);
const filePath = path.resolve(options.filePath);
const trustedRoot = getTrustedRoot(state, filePath);
if (!trustedRoot) {
throw new Error(`Refusing to record a legacy sync path outside trusted roots: ${filePath}`);
}
if (hasUnsafeManagedAncestor(filePath, trustedRoot)) {
throw new Error(`Refusing to manage legacy sync path through symlinked ancestor: ${filePath}`);
}
if (!state.paths.some(entry => entry.path === filePath)) {
const snapshot = snapshotLegacyPath(filePath);
state.paths.push(snapshot);
state.rollbackPaths = [...(state.rollbackPaths || []), { ...snapshot }];
atomicWriteJson(options.statePath, state);
}
}
function rollbackLegacyCodexSync(options) {
const state = readState(options.statePath);
const restoredPaths = [];
const retainedPaths = [];
const rollbackPaths = Array.isArray(state.rollbackPaths) ? state.rollbackPaths : state.paths;View on GitHub (pinned to 8321021c54)