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-targets/kimi-project.js after JSON.parse succeeds but the value is not a plain object — i.e. it is null, an array, or a primitive. The Kimi MCP merge logic iterates object keys via deepMergeJson, so a non-object top level would crash later; this guard fails fast. Same file/label as error 201 (the .mcp.json being merged).
Source
Thrown at scripts/lib/install-targets/kimi-project.js:19
const fs = require('fs');
const path = require('path');
const {
createInstallTargetAdapter,
createManagedOperation,
isForeignPlatformPath,
} = require('./helpers');
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 createMcpMergeOperation(moduleId, repoRoot, targetRoot) {
if (!repoRoot) {
throw new Error('repoRoot is required to plan Kimi MCP configuration');
}
const sourceRelativePath = '.mcp.json';
const sourcePath = path.join(repoRoot, sourceRelativePath);
if (!fs.existsSync(sourcePath) || !fs.statSync(sourcePath).isFile()) {
return null;
}
return createManagedOperation({
kind: 'merge-json',View on GitHub (pinned to 01e15490f0)
Solutions
- Open the named file and ensure the top level is a JSON object literal { ... }.
- Use the standard MCP shape { "mcpServers": { ... } }.
- If the intent is an empty config, write {} rather than [] or null.
- Re-run the Kimi install plan.
Example fix
// before (<repoRoot>/.mcp.json)
[
{ "name": "context7", "command": "npx" }
]
// after
{
"mcpServers": {
"context7": { "command": "npx" }
}
} Defensive patterns
Strategy: type-guard
Validate before calling
const v = JSON.parse(fs.readFileSync('.mcp.json', 'utf8'));
if (!(v !== null && typeof v === 'object' && !Array.isArray(v))) {
throw new Error('.mcp.json top level must be a JSON object');
} Type guard
function isJsonObject(value) {
return value !== null && typeof value === 'object' && !Array.isArray(value);
} Try / catch
try {
readJsonObject(file, label);
} catch (err) {
if (/expected a JSON object/.test(err.message)) {
console.error('Top-level JSON must be an object, not an array or primitive:', file);
}
throw err;
} Prevention
- Always wrap MCP config as { "mcpServers": { ... } }.
- Use a JSON-schema validator in your editor for .mcp.json.
- Treat an empty config as {} — never as [] or null.
When it happens
Trigger: <repoRoot>/.mcp.json contains a JSON array (e.g. [ { ... } ]), a bare string/number/boolean, or null at the top level, but is syntactically valid JSON.
Common situations: User wrapped server definitions in [ ] thinking the file is a list; an empty config was serialized as null instead of {}; a generator emitted a scalar value; merge with a wrong-shape template.
Related errors
- Failed to parse ${label} at ${filePath}: ${error.message}
- repoRoot is required to plan Kimi MCP configuration
- Invalid ${label} at ${filePath}: expected a JSON object
- Kimi requires an explicit --profile choice in this mode.
- Unsupported install target scope: ${scope}
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/b19cb379b99c1be4.
Report an issue: GitHub.