affaan-m/ECC · error
Refusing to apply install operation: missing destination…
Error message
Refusing to apply install operation: missing destination path.
What it means
assertSafeInstallOperation is the safety gate applied before every install operation; it first requires operation.destinationPath to be a non-empty string. A missing or non-string destination path means the plan is malformed and the installer refuses to apply it rather than writing to an unpredictable location. It then continues to check the path is within the trusted target root and not symlinked.
Solutions
- Inspect the failing operation in the plan and set its destinationPath to the absolute install path.
- Regenerate the plan via the adapter's planOperations instead of constructing operations manually.
- Check for a rename (destinationPath vs destRel/absoluteDestination) between plan producer and consumer versions.
- Filter/validate operations for a string destinationPath before calling applyInstallPlan.
Example fix
// before
applyInstallPlan(planWithOp({ sourceRel: 'a.md' }));
// after
applyInstallPlan(planWithOp({ sourceRel: 'a.md', destinationPath: path.join(targetRoot, 'a.md') })); Defensive patterns
Strategy: type-guard
Validate before calling
const invalid = plan.operations.filter(op => !op || typeof op.destinationPath !== 'string');
if (invalid.length) throw new Error(`Plan has ${invalid.length} operations without destinationPath`); Type guard
const hasDestinationPath = (op) => Boolean(op) && typeof op.destinationPath === 'string' && op.destinationPath.length > 0;
Try / catch
try {
await applyInstallPlan(plan);
} catch (e) {
if (e.message.includes('missing destination path')) {
console.error('Plan is malformed: regenerate it via the adapter planner.');
}
throw e;
} Prevention
- Never hand-build plan operations; always produce them through the adapter's planOperations.
- Validate plan shape (each op has a string destinationPath) before applying.
- Watch for renames of destinationPath between plan producer and consumer versions.
When it happens
Trigger: An operation object without destinationPath (null/undefined/non-string) reaching assertSafeInstallOperation via applyInstallPlan, preflightClaudeSettingsOperations, or readOwnedDestinations — usually a plan produced by a broken adapter, partially constructed in tests, or corrupted when serialized/deserialized.
Common situations: Custom tooling generating plan JSON that omits destinationPath; an adapter's planOperations returning an operation missing the field after a refactor; tests hand-building operations with only destinationRel.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- At least one guided harness must be selected
- ${blockingIssues.map(issue => issue.message).join('; ')}
- Refusing to : missing destination path.
- Refusing to trust managed install-state path
- The all/* harness selection cannot be combined with other…
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/ecbca2d764d8b29e.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install/apply.js:249
let changed = false;
updateSettingsAtomic(settingsPath, latestSettings => {
if (hasExplicitCommitAttributionPreference(latestSettings)) {
return { settings: latestSettings };
}
changed = true;
return { settings: withCommitAttributionDisabled(latestSettings) };
}, options);
return changed;
}
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 8321021c54)