affaan-m/ECC · error · Error
Refusing to ${action}: missing destination path.
Error message
Refusing to ${action}: missing destination path. What it means
Thrown by getManagedDestination() in scripts/lib/install-lifecycle.js as a safety guard when the destination path for a managed write is missing or not a string. The installer refuses to proceed with an empty destination because it cannot verify the write lands inside the trusted root.
Source
Thrown at scripts/lib/install-lifecycle.js:232
return parseJsonLikeValue(operation[key], `${operation.kind}.${key}`);
}
}
return undefined;
}
function formatJson(value) {
return `${JSON.stringify(value, null, 2)}\n`;
}
function getManagedDestination(
destinationPath,
trustedRoot,
action,
{ allowFinalSymlink = false } = {}
) {
if (!destinationPath || typeof destinationPath !== 'string') {
throw new Error(`Refusing to ${action}: missing destination path.`);
}
const canonicalRoot = assertWithinTrustedRoot(trustedRoot, trustedRoot, action);
const resolvedDestination = path.resolve(destinationPath);
const canonicalParent = assertWithinTrustedRoot(
path.dirname(resolvedDestination),
canonicalRoot,
action
);
const managedPath = path.join(canonicalParent, path.basename(resolvedDestination));
let stat = null;
try {
stat = fs.lstatSync(managedPath);
} catch (error) {
if (!error || (error.code !== 'ENOENT' && error.code !== 'ENOTDIR')) {
throw error;
}View on GitHub (pinned to 01e15490f0)
Solutions
- Ensure every operation constructed has a non-empty string destinationPath.
- Validate the operation before dispatch: if (!op.destinationPath) throw new Error('destination required').
- Trace the operation builder (buildCopyFileOperation / merge op builders) to confirm destinationPath is computed from defined inputs.
- Default the destination explicitly when deriving from a config field that may be absent.
Example fix
// before
addFileCopyOperation(operations, {
sourceRoot,
sourceRelativePath: 'rules/x.md',
destinationPath: options.dest, // undefined when options.dest missing
});
// after
if (!options.dest) throw new Error('options.dest is required');
addFileCopyOperation(operations, {
sourceRoot,
sourceRelativePath: 'rules/x.md',
destinationPath: options.dest,
}); Defensive patterns
Strategy: validation
Validate before calling
function hasStringDestination(op) {
return op && typeof op.destinationPath === 'string' && op.destinationPath.trim() !== '';
}
if (!hasStringDestination(operation)) {
throw new Error(`Refusing to ${action}: missing destination path.`);
} Type guard
function hasDestinationPath(op) {
return op != null && typeof op === 'object'
&& typeof op.destinationPath === 'string'
&& op.destinationPath.trim() !== '';
} Try / catch
try {
return getManagedDestination(destinationPath, trustedRoot, action, opts);
} catch (err) {
if (/missing destination path/.test(err.message)) {
throw new Error(`Operation ${operation.kind} missing destinationPath`);
}
throw err;
} Prevention
- Validate operations at construction time: assert a non-empty destinationPath.
- Default destinationPath from defined inputs and fail loudly if absent.
- Add a fixture-based test that exercises every operation builder.
When it happens
Trigger: getManagedDestination(destinationPath, trustedRoot, action, opts) where destinationPath is undefined, null, '', or non-string. Reached from copy/merge/symlink operations that forgot to set destinationPath.
Common situations: Operation builder omitted destinationPath; a transform returned undefined for the destination; config has a blank path field; programmatic caller passed options.destinationPath from a missing config key.
Related errors
- Invalid ${label}: ${error.message}
- Invalid ${label}: expected JSON-compatible data
- Refusing to ${action}: destination parent is not a trusted d
- Refusing to read non-file path: ${filePath}
- Missing source file for repair: ${sourcePath || operation.so
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/2dbed32ec08d7be5.
Report an issue: GitHub.