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() after JSON.parse succeeded but the parsed value is not a plain object — it is an array, a primitive (string/number/boolean), or null. The merge-json strategy requires a JSON object root; anything else cannot be merged into the destination.

Source

Thrown at scripts/lib/install-targets/cursor-project.js:32

  if (path.basename(sourceRelativeFile).toLowerCase() === 'readme.md') {
    return null;
  }

  return fileName.endsWith('.md')
    ? `${fileName.slice(0, -3)}.mdc`
    : fileName;
}

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 createJsonMergeOperation({ moduleId, repoRoot, sourceRelativePath, destinationPath }) {
  const sourcePath = path.join(repoRoot, sourceRelativePath);
  if (!fs.existsSync(sourcePath) || !fs.statSync(sourcePath).isFile()) {
    return null;
  }

  return createManagedOperation({
    kind: 'merge-json',
    moduleId,
    sourceRelativePath,
    destinationPath,
    strategy: 'merge-json',
    ownership: 'managed',

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Ensure the source file's top-level value is an object ({...}).
  2. If the data is naturally an array, change the operation strategy from merge-json to flat-file copy.
  3. Wrap the array under a key: { "items": [...] }.
  4. Point the operation at a different source file that contains an object.

Example fix

// before — source file is an array
[ "a", "b" ]
// after — wrap in an object (if a merge is intended)
{ "allowed": ["a", "b"] }
Defensive patterns

Strategy: type-guard

Validate before calling

const value = JSON.parse(fs.readFileSync(filePath, 'utf8'));
if (!value || typeof value !== 'object' || Array.isArray(value)) {
  throw new Error(`${filePath} must contain a JSON object, got ${Array.isArray(value) ? 'array' : typeof value}`);
}

Type guard

/** @param {unknown} v */
function isPlainObject(v) {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}

Prevention

When it happens

Trigger: A source file contains a JSON array ([...]) or a single primitive (e.g. "true") and is referenced by a merge-json operation. Also a file that contains only null.

Common situations: A list-style JSON (e.g. a list of allowed tools) was mistakenly referenced as a merge source; an empty file parsed to undefined; the wrong file was wired into the module's paths.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/3dce563a1ccb6498. Report an issue: GitHub.