affaan-m/ECC · error · Error
Refusing to replace unowned existing file
Error message
Refusing to replace unowned existing file: ${destinationPath} What it means
classifyManagedOperation throws this when the destination file already exists, is not an ECC-owned destination, and is not byte-identical to the operation's copy-file source. ECC will not replace a file it does not own with different content, since that could destroy user work. Only 'create' (missing file), 'managed-update' (owned), 'identical' (same bytes as source), and safe merges are permitted.
Solutions
- Diff the existing file against the ECC source; if the existing content is disposable, delete or move it and re-run the install so ECC creates it fresh.
- Merge your customizations into the ECC version of the file, then re-run so the content is identical or properly owned via a fresh install-state run.
- Restore or regenerate the install-state (guided install) so previously installed files are recognized as owned before updating.
- Keep project-specific files at paths ECC does not manage instead of editing managed files in place.
Example fix
// before: repo's own AGENTS.md blocks install mv AGENTS.md AGENTS.project.md && ecc-install // install creates ECC AGENTS.md // after: fold project notes into the managed file, or keep a separate file ECC never writes
Defensive patterns
Strategy: try-catch
Validate before calling
if (fs.existsSync(destinationPath)
&& !ownedDestinations.has(path.resolve(destinationPath))
&& !fs.readFileSync(destinationPath).equals(fs.readFileSync(operation.sourcePath))) {
console.warn(`${destinationPath} exists and differs from ECC source; move or merge it before installing.`);
} Try / catch
try {
await runInstall(plan);
} catch (err) {
if (String(err.message).startsWith('Refusing to replace unowned existing file:')) {
const file = err.message.replace('Refusing to replace unowned existing file: ', '');
console.error(`Back up or merge ${file}, then re-run the install.`);
} else throw err;
} Prevention
- Diff existing repo files against ECC sources before installing; move project-specific files to non-managed paths.
- Never delete install-state and expect reinstalls to be no-ops; existing unowned files will block.
- Fold customizations into the ECC-managed file via the guided flow so ownership is established.
- Re-install only through the guided preview so ownership and content checks run in order.
When it happens
Trigger: Installing a copy-file operation where the destination file exists, ownedDestinations does not contain its canonical path, and destination content differs from operation.sourcePath content — e.g. installing into a repo that already has its own AGENTS.md/hooks/commands files.
Common situations: Project already defines its own AGENTS.md, hooks.json, or command files before installing ECC; re-installing after install-state was lost/deleted so previous files are no longer 'owned'; installing a newer ECC over files customized locally.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- A normalized install request is required
- An unqualified or incompatible Nasiko executable or receipt…
- Circular install dependency detected at
- forbidden
- Invalid install-state
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/0a04b579091b9d70.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/multi-harness-setup.js:293
const current = assertMergeDestination(destinationPath, destination);
if (ownedDestinations.has(canonicalDestination)) return 'managed-json-update';
const conflicts = findJsonConflicts(current, operation.mergePayload);
if (conflicts.length > 0) {
throw new Error(
`Refusing to overwrite unowned JSON fields at ${destinationPath}: ${conflicts.join(', ')}`
);
}
return 'json-merge';
}
if (ownedDestinations.has(canonicalDestination)) return 'managed-update';
if (
operation.kind === 'copy-file'
&& typeof operation.sourcePath === 'string'
&& readRegularFileSnapshot(operation.sourcePath)?.content.equals(destination.content)
) {
return 'identical';
}
throw new Error(`Refusing to replace unowned existing file: ${destinationPath}`);
}
function writableRequirement(destinationPath) {
if (fs.existsSync(destinationPath)) {
const mode = fs.statSync(destinationPath).isDirectory()
? fs.constants.W_OK | fs.constants.X_OK
: fs.constants.W_OK;
return { candidatePath: destinationPath, mode };
}
let candidatePath = path.dirname(destinationPath);
while (!fs.existsSync(candidatePath)) {
const parentPath = path.dirname(candidatePath);
if (parentPath === candidatePath) break;
candidatePath = parentPath;
}
return {
candidatePath,View on GitHub (pinned to 8321021c54)