affaan-m/ECC · error · Error
Cannot merge ECC configuration into invalid JSON at
Error message
Cannot merge ECC configuration into invalid JSON at ${destinationPath}: ${error.message} What it means
assertMergeDestination parses an existing merge-json destination file as JSON before ECC merges its configuration into it. If JSON.parse throws, the error is rethrown with this message naming the destination and the underlying parse error. ECC refuses to merge into malformed JSON because the merge would either fail or silently destroy the invalid content.
Solutions
- Open the destination file, fix the JSON syntax error reported in error.message (line/column), then re-run the install.
- If the file's content is disposable, delete it so the installer recreates it, then reapply intended settings as valid JSON.
- Convert comments to valid JSON (or move the file to a JSONC path the tool actually supports) before installing.
Example fix
// before: agents.json
{ "name": "x", } // trailing comma
// after
{ "name": "x" } Defensive patterns
Strategy: validation
Validate before calling
function assertValidJsonObjectFile(p) {
if (!fs.existsSync(p)) return;
const parsed = JSON.parse(fs.readFileSync(p, 'utf8')); // throws with position info
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error(`${p} must contain a JSON object`);
}
}
assertValidJsonObjectFile(destinationPath); Type guard
function isJsonObjectFile(p) {
try {
const v = JSON.parse(fs.readFileSync(p, 'utf8'));
return Boolean(v) && typeof v === 'object' && !Array.isArray(v);
} catch { return false; }
} Try / catch
try {
await runInstall(plan);
} catch (err) {
if (String(err.message).startsWith('Cannot merge ECC configuration into invalid JSON')) {
const file = err.message.match(/at (.+?):/)[1];
console.error(`Fix JSON syntax in ${file} or delete it and re-run.`);
} else throw err;
} Prevention
- Validate config JSON files (node -e "JSON.parse(...)" or a linter) before running installs.
- Never put comments or trailing commas in .json files; use proper JSONC paths if supported.
- Write config files atomically (temp file + rename) to avoid truncated JSON.
- Commit valid config files so a broken local edit is caught in review/CI.
When it happens
Trigger: Calling an install/plan operation with kind 'merge-json' (via classifyManagedOperation → assertMergeDestination) where the destination file exists but contains syntactically invalid JSON — trailing commas, comments, truncated writes, or a non-JSON file placed at the config path.
Common situations: A user hand-edited a harness config (e.g. agents/openai.yaml-style JSON or .mcp.json) and introduced a syntax error; a previous crashed write left a truncated file; someone renamed a JSONC file to .json keeping comments.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to parse
- Invalid JSON in at
- Invalid JSON in
- candidate configuration exceeds 1 MiB
- Cannot merge ECC configuration at
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/3d07bbdca53a8c9d.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/multi-harness-setup.js:244
) {
throw new Error(
`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, existingSnapshot = null) {
const snapshot = existingSnapshot || readRegularFileSnapshot(destinationPath);
if (!snapshot) return null;
let current;
try {
current = JSON.parse(snapshot.content.toString('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)) {View on GitHub (pinned to 8321021c54)