affaan-m/ECC · critical · Error
Refusing to apply install operation: missing destination pat
Error message
Refusing to apply install operation: missing destination path.
What it means
Thrown by assertSafeInstallOperation in scripts/lib/install/apply.js. The guard is invoked twice per operation inside applyInstallPlan (before and after mkdirSync) and its first check is that operation is truthy and operation.destinationPath is a string. Without a destination path the installer cannot confine the write to a trusted root, so it refuses rather than guessing. This is a fail-closed security guard, not a normal validation error.
Source
Thrown at scripts/lib/install/apply.js:198
return value;
}
function findHooksOperation(plan, hooksDestinationPath) {
return plan.operations.find(item => (
item.destinationPath === hooksDestinationPath
&& item.moduleId === 'hooks-runtime'
&& typeof item.sourcePath === 'string'
));
}
function isMcpConfigPath(filePath) {
const basename = path.basename(String(filePath || ''));
return basename === '.mcp.json' || basename === 'mcp.json';
}
function assertSafeInstallOperation(plan, operation) {
if (!operation || typeof operation.destinationPath !== 'string') {
throw new Error('Refusing to apply install operation: missing destination path.');
}
const targetRoot = plan && plan.targetRoot;
assertWithinTrustedRoot(operation.destinationPath, targetRoot, 'install ECC file');
const resolvedRoot = path.resolve(targetRoot);
const resolvedTarget = path.resolve(operation.destinationPath);
const relativePath = path.relative(resolvedRoot, resolvedTarget);
const segments = relativePath ? relativePath.split(path.sep) : [];
for (const segmentIndex of Array.from({ length: segments.length + 1 }, (_value, index) => index)) {
const currentPath = segmentIndex === 0
? resolvedRoot
: path.join(resolvedRoot, ...segments.slice(0, segmentIndex));
try {
const stats = fs.lstatSync(currentPath);
if (stats.isSymbolicLink()) {
throw new Error(
`Refusing to install ECC file through symlinked path: '${currentPath}'.`View on GitHub (pinned to 01e15490f0)
Solutions
- Inspect plan.operations and confirm every entry has a non-empty string destinationPath.
- Build operations through createManagedOperation (helpers.js) so destinationPath is always set.
- If you are replaying an old install-state, regenerate the plan from the current ECC version instead of reusing the file.
- Add a unit test that asserts every emitted operation passes a destinationPath string check.
Example fix
// before
plan.operations.push({ kind: 'copy-file', sourcePath: src });
// after
plan.operations.push({
kind: 'copy-file',
sourcePath: src,
destinationPath: path.join(targetRoot, 'file.txt'),
}); Defensive patterns
Strategy: validation
Validate before calling
function assertOperationsWellFormed(operations) {
for (const op of operations) {
if (!op || typeof op.destinationPath !== 'string' || op.destinationPath.length === 0) {
throw new Error(`Operation missing destinationPath: ${JSON.stringify(op)}`);
}
}
}
assertOperationsWellFormed(plan.operations); Type guard
function isInstallOperation(value) {
return Boolean(
value && typeof value === 'object'
&& typeof value.destinationPath === 'string'
&& typeof value.kind === 'string'
);
} Try / catch
try {
applyInstallPlan(plan);
} catch (err) {
if (/missing destination path/.test(err.message)) {
console.error('Malformed operations:', plan.operations.filter(o => !o || typeof o.destinationPath !== 'string'));
}
throw err;
} Prevention
- Never hand-construct operation objects; always use createManagedOperation from helpers.js.
- Add a unit test asserting every emitted operation has a string destinationPath.
- Do not replay install-state files across ECC versions without re-validating the schema.
When it happens
Trigger: A plan whose operations array contains a null entry, an object missing destinationPath, or an object whose destinationPath is undefined/a number/an empty value. Happens with hand-built operation objects or a stale install-state replayed under a renamed schema.
Common situations: Custom adapter that emits operations without going through createManagedOperation; an old install-state file whose field names no longer match; a programmatic plan assembled by hand without the destinationPath field.
Related errors
- Invalid ECC repo root: missing install script at ${installAp
- TypeScript compiler not found. Install root dev dependencies
- Missing value for --target
- Unknown catalog command: ${options.command}
- Failed to read ${path.basename(filePath)}: ${error.message}
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/ecbca2d764d8b29e.
Report an issue: GitHub.