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() adds a file to the sync-state's managed set. It resolves the path and asks getTrustedRoot() for a root (codexHome, recorded trustedRoots, or installedHooksPath) that contains it; with no match it refuses, so the state file can never record paths outside the directories ECC was told it owns.
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 06c5e118c4)
Solutions
- Derive the path from the same codexHome passed to beginLegacySyncState(), e.g. path.join(codexHome, 'config.toml')
- If the path legitimately lives elsewhere, pass it via installedHooksPath (or trustedRoots) at begin time so it becomes trusted
- Fix the caller to always pass absolute paths built under the managed roots
Example fix
// before: recordLegacySyncPath({ statePath, filePath: 'config.toml' }) // resolves against cwd -> refused
recordLegacySyncPath({ statePath, filePath: path.join(codexHome, 'config.toml') }); Defensive patterns
Strategy: validation
Validate before calling
const path = require('path');
function isUnderTrustedRoot(filePath, trustedRoots) {
const abs = path.resolve(filePath);
return trustedRoots.some(r => abs === r || abs.startsWith(path.resolve(r) + path.sep));
}
if (!isUnderTrustedRoot(filePath, trustedRoots)) throw new Error('caller bug: path not under a trusted root: ' + filePath); Prevention
- Always build managed paths with path.join(codexHome, ...)
- Pass one shared options object through begin/record calls
- Never record relative paths
When it happens
Trigger: Calling recordLegacySyncPath({ statePath, filePath }) where path.resolve(filePath) lands outside codexHome and the hooks path — commonly a relative path that resolved against process.cwd(), or a codexHome option that differs from the one used at beginLegacySyncState() time.
Common situations: Caller computes the managed path from the repo root instead of codexHome; options objects built in two places with different codexHome values; wrong variable passed as filePath.
Related errors
- Invalid ${flag}: expected a single cache path segment
- Refusing to reuse unsafe legacy Codex ownership path: ${file
- Refusing to manage legacy sync path through symlinked ancest
- Unknown argument: ${arg}
- Missing value for ${arg}
AI-assisted analysis of affaan-m/ECC@06c5e118c4 (2026-08-18).
Data as JSON: /api/errors/4081be85c9d64612.
Report an issue: GitHub.