affaan-m/ECC · error
Invalid at : expected a JSON object
Error message
Invalid ${label} at ${filePath}: expected a JSON object What it means
readJsonObject parses a JSON file and requires the result to be a non-null, non-array plain object, because installer config files (like .mcp.json or settings JSON) must be key/value maps to merge safely. Nulls, arrays, and scalars are rejected with a message naming the file's label and path. A parse failure itself throws a separate wrapped error; this one is purely about the parsed shape.
Solutions
- Edit the file so its top level is a JSON object: wrap contents in { } and use key/value pairs.
- If the file is an empty array, replace it with {}.
- Validate with JSON.parse plus a typeof check before handing the path to the installer.
- Check which tool rewrote the file and fix its serialization to emit an object.
Example fix
// before: .mcp.json
[]
// after
{
"mcpServers": {}
} Defensive patterns
Strategy: validation
Validate before calling
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error(`${filePath} must contain a top-level JSON object`);
} Type guard
const isJsonObject = (v) => Boolean(v) && typeof v === 'object' && !Array.isArray(v);
Try / catch
try {
const cfg = readJsonObject(p, 'MCP config');
} catch (e) {
if (e.message.includes('expected a JSON object')) {
console.error(`${p}: rewrite the file with a top-level { } object.`);
}
throw e;
} Prevention
- Validate config JSON shape (object, not array/scalar) after any tool rewrites it.
- Reset broken configs to {} rather than [] when clearing them.
- Add a JSON schema check for .mcp.json / settings files in CI or a pre-install lint.
When it happens
Trigger: readJsonObject(filePath, label) reading a file whose top-level JSON is an array ('[...]'), a string/number/boolean literal, or literal 'null' — e.g. a settings file saved as '[]' or a config overwritten with '"text"'.
Common situations: A .mcp.json or settings file that was reset to an empty array; a user replacing an object config with a list of servers; a script serializing an array where an object map was expected; hand-edited JSON losing its root braces.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- candidate configuration exceeds 1 MiB
- input_artifacts must be a list of local paths
- application config exceeds local size limit
- application request must be a JSON object
- Asset request_id/modality does not match the bundle
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/9de5c7995fc50087.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install/apply.js:84
destRel: path.relative(plan.targetRoot, operation.destinationPath),
});
}
}
return buildInstallIndex(mappings);
}
function readJsonObject(filePath, label) {
let parsed;
try {
parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch (error) {
const wrappedError = new Error(`Failed to parse ${label} at ${filePath}: ${error.message}`);
wrappedError.code = error.code;
throw wrappedError;
}
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error(`Invalid ${label} at ${filePath}: expected a JSON object`);
}
return parsed;
}
function readOptionalJsonObject(filePath, label) {
try {
return readJsonObject(filePath, label);
} catch (error) {
if (error.code === 'ENOENT') {
return {};
}
throw error;
}
}
function readInstalledFileNoFollow(plan, operation) {
assertSafeInstallOperation(plan, operation);View on GitHub (pinned to 8321021c54)