affaan-m/ECC · error · Error
Invalid ${label} at ${filePath}: expected a JSON object
Error message
Invalid ${label} at ${filePath}: expected a JSON object What it means
Thrown by readJsonObject in scripts/lib/install/apply.js after JSON.parse succeeds but the value is not a plain object (null, array, or primitive). The apply pipeline needs an object so it can deepMergeJson (for merge-json) or iterate keys; a non-object top level is refused.
Source
Thrown at scripts/lib/install/apply.js:53
mappings.push({
sourceRel: operation.sourceRelativePath,
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) {
throw new Error(`Failed to parse ${label} at ${filePath}: ${error.message}`);
}
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error(`Invalid ${label} at ${filePath}: expected a JSON object`);
}
return parsed;
}
function stateWithContentDigests(state) {
return {
...state,
operations: (state.operations || []).map(operation => {
if (
!operation.destinationPath
|| !fs.existsSync(operation.destinationPath)
|| !fs.statSync(operation.destinationPath).isFile()
) {
return { ...operation };
}
return {
...operation,View on GitHub (pinned to 01e15490f0)
Solutions
- Open the named file and make the top level a JSON object { ... }.
- For empty configs use {} rather than [] or null.
- Re-run the install.
Example fix
// before (existing ~/.claude/.mcp.json)
[]
// after
{ "mcpServers": {} } Defensive patterns
Strategy: type-guard
Validate before calling
function preflightJsonObject(filePath) {
if (!fs.existsSync(filePath)) return;
const v = JSON.parse(fs.readFileSync(filePath, 'utf8'));
if (!(v !== null && typeof v === 'object' && !Array.isArray(v))) {
throw new Error(`${filePath} top level must be a JSON object`);
}
}
['.mcp.json', 'mcp.json'].forEach(f => preflightJsonObject(path.join(targetRoot, f))); Type guard
function isJsonObject(v) {
return v !== null && typeof v === 'object' && !Array.isArray(v);
} Prevention
- Keep the top level of every ECC-managed JSON file as an object.
- Use {} for empty configs — never [] or null.
- Audit tools that write into .mcp.json to confirm they emit objects.
When it happens
Trigger: An existing destination .mcp.json / mcp.json that is a JSON array or scalar; a source hooks file whose top level is an array.
Common situations: User replaced .mcp.json with []; another tool wrote a bare string; a generator emitted null for an empty config.
Related errors
- Invalid ${label} at ${filePath}: expected a JSON object
- Failed to parse ${label} at ${filePath}: ${error.message}
- Failed to parse ${label} at ${filePath}: ${error.message}
- repoRoot is required to plan Kimi MCP configuration
- Invalid hooks config at ${hooksSourcePath}: expected "hooks"
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/9de5c7995fc50087.
Report an issue: GitHub.