{"record":{"id":"79422f93b2aa5a6a","repo":"affaan-m/ECC","slug":"refusing-to-overwrite-unowned-json-fields-at-des","errorCode":null,"errorMessage":"Refusing to overwrite unowned JSON fields at ${destinationPath}: ${conflicts.join(', ')}","messagePattern":"Refusing to overwrite unowned JSON fields at (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/multi-harness-setup.js","lineNumber":279,"sourceCode":"    const field = prefix ? `${prefix}.${key}` : key;\n    if (isPlainObject(currentValue) && isPlainObject(patchValue)) {\n      return findJsonConflicts(currentValue, patchValue, field);\n    }\n    return JSON.stringify(currentValue) === JSON.stringify(patchValue) ? [] : [field];\n  });\n}\n\nfunction classifyManagedOperation(operation, ownedDestinations) {\n  const destinationPath = operation.destinationPath;\n  const destination = readRegularFileSnapshot(destinationPath);\n  if (!destination) return 'create';\n  const canonicalDestination = canonicalPath(destinationPath);\n  if (operation.kind === 'merge-json') {\n    const current = assertMergeDestination(destinationPath, destination);\n    if (ownedDestinations.has(canonicalDestination)) return 'managed-json-update';\n    const conflicts = findJsonConflicts(current, operation.mergePayload);\n    if (conflicts.length > 0) {\n      throw new Error(\n        `Refusing to overwrite unowned JSON fields at ${destinationPath}: ${conflicts.join(', ')}`\n      );\n    }\n    return 'json-merge';\n  }\n  if (ownedDestinations.has(canonicalDestination)) return 'managed-update';\n  if (\n    operation.kind === 'copy-file'\n    && typeof operation.sourcePath === 'string'\n    && readRegularFileSnapshot(operation.sourcePath)?.content.equals(destination.content)\n  ) {\n    return 'identical';\n  }\n  throw new Error(`Refusing to replace unowned existing file: ${destinationPath}`);\n}\n\nfunction writableRequirement(destinationPath) {\n  if (fs.existsSync(destinationPath)) {","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/multi-harness-setup.js#L261-L297","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Review the listed conflicting fields and manually align them with ECC's intended values, or set your own values after install.","Back up the file, let the install claim it (establishing ownership via the guided flow), then reapply your customizations.","Regenerate install-state (delete state file, re-run guided install) so existing files become owned destinations instead of unowned conflicts.","Split your custom config into a separate file/key that ECC's merge payload does not touch."],"exampleFix":"// before: user changed mcpServers.context7.url in an unowned config\n// after: remove/rename your custom key so the merge applies cleanly\n{ \"mcpServers\": { \"context7\": { \"url\": \"https://default.example\" } }, \"myCustomOverrides\": { \"url\": \"https://mine.example\" } }","handlingStrategy":"try-catch","validationCode":"const existing = JSON.parse(fs.readFileSync(destinationPath, 'utf8'));\nconst conflicts = findJsonConflicts(existing, operation.mergePayload); // same recursive diff the lib uses\nif (conflicts.length) console.warn('Conflicting keys to resolve before install:', conflicts);","typeGuard":null,"tryCatchPattern":"try {\n  await runInstall(plan);\n} catch (err) {\n  if (String(err.message).startsWith('Refusing to overwrite unowned JSON fields')) {\n    const fields = err.message.split(': ')[1];\n    console.error(`Resolve these keys manually or back up the file: ${fields}`);\n  } else throw err;\n}","preventionTips":["Diff your config against ECC's intended merge payload before installing.","Keep personal overrides in separate keys/files the merge payload never touches.","Back up config files before running installs into an existing repo.","Re-run the guided install rather than hand-editing managed configs so ownership stays consistent."],"tags":["config","merge-conflict","json"],"backgroundTag":"conflicting-config-options","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}