affaan-m/ECC · error · Error
Refusing to ${action} outside the install root: '${target}'
Error message
Refusing to ${action} outside the install root: '${target}' is not within '${root}'. What it means
Thrown by assertWithinTrustedRoot when the target, after realpath canonicalization, does not resolve to within the trusted root. This is the core path-traversal / symlink-escape defense: it defeats ../ sequences and symlinks that point out of the install root, blocking writes outside the trusted tree.
Source
Thrown at scripts/lib/path-safety.js:100
* Fail-closed guard: throw unless `target` is contained within `root`.
* Returns the canonicalized target path on success.
*/
function assertWithinTrustedRoot(target, root, action = 'write') {
if (!target || typeof target !== 'string') {
throw new Error(`Refusing to ${action}: missing destination path.`);
}
if (!root) {
throw new Error(`Refusing to ${action} '${target}': no trusted install root resolved.`);
}
let containment;
try {
containment = resolveContainment(target, root);
} catch {
containment = null;
}
if (!containment || !containment.contained) {
throw new Error(`Refusing to ${action} outside the install root: '${target}' is not within '${root}'.`);
}
return containment.realTarget;
}
module.exports = {
realpathNearestExisting,
isWithinRoot,
assertWithinTrustedRoot
};
View on GitHub (pinned to 01e15490f0)
Solutions
- Confirm the install-state file belongs to the current project root before replaying it.
- Use isWithinRoot(target, root) to filter state entries, skipping any that escape rather than throwing.
- If a legitimate destination moved, re-run install to regenerate the state file against the current root.
- Audit the state file for absolute paths or ../ segments and regenerate it if any are present.
Example fix
// before
for (const op of state.ops) {
assertWithinTrustedRoot(op.destinationPath, root, 'write'); // throws on escape
}
// after
for (const op of state.ops) {
if (!isWithinRoot(op.destinationPath, root)) {
console.warn('Skipping out-of-root destination:', op.destinationPath);
continue;
}
assertWithinTrustedRoot(op.destinationPath, root, 'write');
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!isWithinRoot(target, root)) {
console.warn('Skipping out-of-root path:', target);
continue;
}
assertWithinTrustedRoot(target, root, action); Type guard
const { isWithinRoot } = require('./path-safety');
// isWithinRoot returns boolean (false on any error) instead of throwing. Prevention
- Filter install-state entries with isWithinRoot before asserting.
- Regenerate the state file if the project root moved.
- Audit recorded paths for ../ segments or absolute paths outside the root.
When it happens
Trigger: A target containing '..' that escapes the root; a symlink inside the install tree pointing to a directory outside it; a recorded destinationPath that was captured under a different root than the current trusted root; an absolute path to an unrelated location.
Common situations: A cloned/forked repo ships a crafted .cursor/ecc-install-state.json with destinations pointing outside the project; a symlink in ~/.claude pointing elsewhere; a repair replay after the project moved; an uninstall whose recorded paths predate a root change.
Related errors
- Refusing to ${action}: missing destination path.
- Refusing to ${action} '${target}': no trusted install root r
- TypeScript compiler not found. Install root dev dependencies
- ${source} is missing the expected catalog marker
- Invalid ${flag}: expected a single cache path segment
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/de2a0c8fd10c542d.
Report an issue: GitHub.