affaan-m/ECC · error · Error
Cannot merge ECC configuration at ${destinationPath}: expect
Error message
Cannot merge ECC configuration at ${destinationPath}: expected a JSON object. What it means
Thrown by assertMergeDestination when the destination file parses as JSON but is not a JSON object — i.e., it is null, an array, or a non-object primitive. merge-json merges ECC's payload into an object root; merging into an array or scalar is undefined and would destroy the existing structure, so the installer refuses. This is a type guard on the shape of the existing config.
Source
Thrown at scripts/lib/multi-harness-setup.js:197
`Refusing unverified ownership from install-state at ${plan.installStatePath}: `
+ `content digest does not match ${destinationPath}.`
);
}
destinations.add(canonicalDestination);
}
return { destinations, stateFingerprint: validatedFingerprint };
}
function assertMergeDestination(destinationPath) {
if (!fs.existsSync(destinationPath)) return null;
let current;
try {
current = JSON.parse(fs.readFileSync(destinationPath, 'utf8'));
} catch (error) {
throw new Error(`Cannot merge ECC configuration into invalid JSON at ${destinationPath}: ${error.message}`);
}
if (!current || typeof current !== 'object' || Array.isArray(current)) {
throw new Error(`Cannot merge ECC configuration at ${destinationPath}: expected a JSON object.`);
}
return current;
}
function isPlainObject(value) {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}
function findJsonConflicts(current, patch, prefix = '') {
if (!isPlainObject(patch)) return [];
return Object.entries(patch).flatMap(([key, patchValue]) => {
if (!Object.prototype.hasOwnProperty.call(current, key)) return [];
const currentValue = current[key];
const field = prefix ? `${prefix}.${key}` : key;
if (isPlainObject(currentValue) && isPlainObject(patchValue)) {
return findJsonConflicts(currentValue, patchValue, field);
}
return JSON.stringify(currentValue) === JSON.stringify(patchValue) ? [] : [field];View on GitHub (pinned to 01e15490f0)
Solutions
- Inspect destinationPath: if it should be an object, rewrite it as {} or a proper object literal and re-run.
- If the file legitimately holds an array/scalar, it cannot be a merge-json target — exclude it from the managed profile or change the operation kind.
- If the file is null/empty, replace it with {} so ECC can merge into it.
- Verify the ECC catalog entry for this destination is correct (kind should be merge-json only for object configs).
Example fix
// before: destination contains an array
// file: ["a", "b"]
classifyManagedOperation(op, owned); // throws [290]
// after: make it an object root
// file: { "items": ["a", "b"] } Defensive patterns
Strategy: type-guard
Validate before calling
const fs = require('fs');
function assertMergeDestinationIsObject(destinationPath) {
if (!fs.existsSync(destinationPath)) return;
const v = JSON.parse(fs.readFileSync(destinationPath, 'utf8'));
if (!v || typeof v !== 'object' || Array.isArray(v)) {
throw new Error(`Cannot merge into ${destinationPath}: root is ${Array.isArray(v) ? 'array' : typeof v}, expected a JSON object.`);
}
}
assertMergeDestinationIsObject(operation.destinationPath); Type guard
function isJsonObjectFile(filePath) {
if (!require('fs').existsSync(filePath)) return true;
const v = JSON.parse(require('fs').readFileSync(filePath, 'utf8'));
return Boolean(v) && typeof v === 'object' && !Array.isArray(v);
} Try / catch
try {
classifyManagedOperation(operation, ownedDestinations);
} catch (err) {
if (/expected a JSON object/.test(err.message)) {
throw new Error(`${operation.destinationPath} is not a JSON object; convert it or remove it before merge.`);
}
throw err;
} Prevention
- Keep merge-target config files as objects at the root, never arrays or scalars.
- If a file is naturally an array, do not register it as a merge-json destination.
- Replace null/empty config files with {} before install.
- Review the ECC catalog entry for the destination kind.
When it happens
Trigger: Fires when the parsed `current` is falsy, typeof !== 'object', or Array.isArray(current) for a merge-json destination. Occurs when a settings file that ECC expects to be an object is actually a JSON array (e.g., a TSConfig 'extends' list stored standalone), a bare string/number, or the literal null.
Common situations: A file like .eslintignore or a JSON array config living at a path ECC treats as an object merge target; null written by another tool to 'clear' a config; a generated manifest that is an array at the root; mislabeling an operation kind (merge-json) for a destination that is fundamentally not an object.
Related errors
- Cannot merge ECC configuration into invalid JSON at ${destin
- Invalid Kimi install profile: ${profile}
- Refusing to overwrite unowned JSON fields at ${destinationPa
- Invalid mode "${mode}". Allowed modes: ${allowedModes.join('
- Missing merge payload for repair: ${operation.destinationPat
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/11fd29f9df670828.
Report an issue: GitHub.