affaan-m/ECC · error

Invalid at : expected a JSON object

Error message

Invalid ${label} at ${filePath}: expected a JSON object

What it means

readJsonObject in the Kimi project target requires the parsed JSON root to be a plain object; null, arrays, and primitives are rejected because MCP merge needs an object to merge `mcpServers` into. The message includes the labeled file path so the offending file is easy to find.

Solutions

  1. Open the file at filePath and change the top-level value to a JSON object
  2. If the file holds an array of servers, wrap it: `{ "mcpServers": { ... } }` mapping names to server objects
  3. Replace `null`/`[]` placeholders with `{}` and re-run the install

Example fix

// before
[ { "name": "context7", "command": "npx" } ]
// after
{ "mcpServers": { "context7": { "command": "npx" } } }
Defensive patterns

Strategy: type-guard

Validate before calling

// Pre-check root shape of the Kimi MCP config
const parsed = JSON.parse(fs.readFileSync(kimiMcpPath, 'utf8'));
if (parsed === null || Array.isArray(parsed) || typeof parsed !== 'object') {
  console.error(`${kimiMcpPath} must be a JSON object at the top level`);
}

Type guard

function isJsonObject(v) {
  return v !== null && typeof v === 'object' && !Array.isArray(v);
}

Try / catch

try {
  runInstallOperation(op);
} catch (e) {
  if (/Invalid .* at .*: expected a JSON object/.test(e.message)) {
    // convert the file root to an object and re-run the install
  } else throw e;
}

Prevention

When it happens

Trigger: createMcpMergeOperation reads a Kimi project config file whose contents are valid JSON but whose top-level value is an array (e.g. `[]`), `null`, a string, or a number.

Common situations: A user initialized the Kimi MCP config as a JSON array of servers instead of an object; a template or script wrote `null` as a placeholder; an export tool produced a list-shaped config.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/b19cb379b99c1be4. Report an issue: GitHub.

Appendix: 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 8321021c54)