{"record":{"id":"11fd29f9df670828","repo":"affaan-m/ECC","slug":"cannot-merge-ecc-configuration-at-destinationpat","errorCode":null,"errorMessage":"Cannot merge ECC configuration at ${destinationPath}: expected a JSON object.","messagePattern":"Cannot merge ECC configuration at (.+?): expected a JSON object\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/multi-harness-setup.js","lineNumber":247,"sourceCode":"        + `content digest does not match ${destinationPath}.`\n      );\n    }\n    destinations.add(canonicalDestination);\n  }\n  return { destinations, stateFingerprint: validatedFingerprint };\n}\n\nfunction assertMergeDestination(destinationPath, existingSnapshot = null) {\n  const snapshot = existingSnapshot || readRegularFileSnapshot(destinationPath);\n  if (!snapshot) return null;\n  let current;\n  try {\n    current = JSON.parse(snapshot.content.toString('utf8'));\n  } catch (error) {\n    throw new Error(`Cannot merge ECC configuration into invalid JSON at ${destinationPath}: ${error.message}`);\n  }\n  if (!current || typeof current !== 'object' || Array.isArray(current)) {\n    throw new Error(`Cannot merge ECC configuration at ${destinationPath}: expected a JSON object.`);\n  }\n  return current;\n}\n\nfunction isPlainObject(value) {\n  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction findJsonConflicts(current, patch, prefix = '') {\n  if (!isPlainObject(patch)) return [];\n  return Object.entries(patch).flatMap(([key, patchValue]) => {\n    if (!Object.prototype.hasOwnProperty.call(current, key)) return [];\n    const currentValue = current[key];\n    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];","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/multi-harness-setup.js#L229-L265","documentation":"After successfully parsing, assertMergeDestination requires the parsed value to be a non-null, non-array object, because ECC merges key/value configuration into it. This error is thrown when the destination JSON parses to null, an array, a string, a number, or a boolean instead of an object. Merging into such values is not defined, so the operation is rejected.","triggerScenarios":"A merge-json destination file contains valid JSON but not an object — e.g. the file holds '[ ]', '\"text\"', 'null', or a top-level number — reached via classifyManagedOperation during install planning.","commonSituations":"A user replaced config content with a JSON array of items; a script wrote 'null' to reset a config; config file accidentally contains just a JSON string; empty-array placeholder written by another tool.","solutions":["Wrap the file's content in a top-level object (e.g. { \"items\": [...] }) and re-run the install.","Delete the file so the installer creates a fresh JSON object config, then port over any array/string data under a named key.","Verify the destination path is the config file ECC expects, not a data file that legitimately holds a non-object."],"exampleFix":"// before: config.json\n[\"agent-a\", \"agent-b\"]\n// after\n{ \"agents\": [\"agent-a\", \"agent-b\"] }","handlingStrategy":"type-guard","validationCode":"if (fs.existsSync(destinationPath)) {\n  const v = JSON.parse(fs.readFileSync(destinationPath, 'utf8'));\n  if (!v || typeof v !== 'object' || Array.isArray(v)) {\n    throw new Error(`${destinationPath} must be a JSON object before install`);\n  }\n}","typeGuard":"function isJsonObject(v) {\n  return Boolean(v) && typeof v === 'object' && !Array.isArray(v);\n}\n// guard: isJsonObject(JSON.parse(fs.readFileSync(destinationPath, 'utf8')))","tryCatchPattern":"try {\n  await runInstall(plan);\n} catch (err) {\n  if (String(err.message).includes('expected a JSON object')) {\n    const file = err.message.match(/at (.+?):/)[1];\n    fs.writeFileSync(file, '{}'); // or fix the shape manually, then re-run\n  } else throw err;\n}","preventionTips":["Keep merge-json destinations as top-level objects; nest arrays under named keys.","Never write null/arrays/strings as entire config file contents.","Add a pre-install check asserting config shapes.","Document config file shape for the team to prevent accidental rewrites."],"tags":["json","config","shape-mismatch"],"backgroundTag":"unexpected-response-shape","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"}