affaan-m/ECC · error · Error

Refusing to overwrite unowned JSON fields at

Error message

Refusing to overwrite unowned JSON fields at ${destinationPath}: ${conflicts.join(', ')}

What it means

For a merge-json operation whose destination is not yet an ECC-owned destination, findJsonConflicts compares the existing JSON with the merge payload. If any existing key (recursively) would be changed to a different value, classifyManagedOperation throws this error listing the conflicting field paths. ECC refuses to silently overwrite user-set values in files it does not own.

Solutions

  1. Review the listed conflicting fields and manually align them with ECC's intended values, or set your own values after install.
  2. Back up the file, let the install claim it (establishing ownership via the guided flow), then reapply your customizations.
  3. Regenerate install-state (delete state file, re-run guided install) so existing files become owned destinations instead of unowned conflicts.
  4. Split your custom config into a separate file/key that ECC's merge payload does not touch.

Example fix

// before: user changed mcpServers.context7.url in an unowned config
// after: remove/rename your custom key so the merge applies cleanly
{ "mcpServers": { "context7": { "url": "https://default.example" } }, "myCustomOverrides": { "url": "https://mine.example" } }
Defensive patterns

Strategy: try-catch

Validate before calling

const existing = JSON.parse(fs.readFileSync(destinationPath, 'utf8'));
const conflicts = findJsonConflicts(existing, operation.mergePayload); // same recursive diff the lib uses
if (conflicts.length) console.warn('Conflicting keys to resolve before install:', conflicts);

Try / catch

try {
  await runInstall(plan);
} catch (err) {
  if (String(err.message).startsWith('Refusing to overwrite unowned JSON fields')) {
    const fields = err.message.split(': ')[1];
    console.error(`Resolve these keys manually or back up the file: ${fields}`);
  } else throw err;
}

Prevention

When it happens

Trigger: Running an install/update where a merge-json destination exists, is absent from the owned-destinations set (no trusted install-state coverage), and at least one key in operation.mergePayload has a different existing value — e.g. user edited a shared config key ECC also wants to set.

Common situations: User customized a key in .mcp.json or a harness config that a new ECC module also writes; installing ECC into a repo where another tool set the same config keys; re-installing after manually editing managed config without refreshing ownership state.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/79422f93b2aa5a6a. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/multi-harness-setup.js:279

    const field = prefix ? `${prefix}.${key}` : key;
    if (isPlainObject(currentValue) && isPlainObject(patchValue)) {
      return findJsonConflicts(currentValue, patchValue, field);
    }
    return JSON.stringify(currentValue) === JSON.stringify(patchValue) ? [] : [field];
  });
}

function classifyManagedOperation(operation, ownedDestinations) {
  const destinationPath = operation.destinationPath;
  const destination = readRegularFileSnapshot(destinationPath);
  if (!destination) return 'create';
  const canonicalDestination = canonicalPath(destinationPath);
  if (operation.kind === 'merge-json') {
    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)) {

View on GitHub (pinned to 8321021c54)