affaan-m/ECC · error · Error

${label} is invalid at ${filePath}: expected a JSON object

Error message

${label} is invalid at ${filePath}: expected a JSON object

What it means

Thrown by readJsonObject in scripts/lib/install/inventory.js after JSON.parse succeeds but the value is not a plain object (null, array, or primitive). The inventory reader needs an object so it can read state.target, state.operations, and state.resolution.selectedModules.

Source

Thrown at scripts/lib/install/inventory.js:39

    || path.join(homeDir, '.claude');
  const projectRoot = options.projectRoot || process.cwd();

  return {
    homeDir: path.resolve(homeDir),
    configDir: path.resolve(configDir),
    projectRoot: path.resolve(projectRoot),
  };
}

function readJsonObject(filePath, label) {
  let value;
  try {
    value = JSON.parse(fs.readFileSync(filePath, 'utf8'));
  } catch (error) {
    throw new Error(`${label} is invalid at ${filePath}: ${error.message}`);
  }
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    throw new Error(`${label} is invalid at ${filePath}: expected a JSON object`);
  }
  return value;
}

function findManualClaudePlugin(options = {}) {
  const { configDir } = resolveClaudePaths(options);
  const pluginsDir = path.join(configDir, 'plugins');
  const candidates = [
    ['ecc', '.claude-plugin', 'plugin.json'],
    ['ecc', 'plugin.json'],
    ['ecc@ecc', '.claude-plugin', 'plugin.json'],
    ['ecc@ecc', 'plugin.json'],
    ['everything-claude-code', '.claude-plugin', 'plugin.json'],
    ['everything-claude-code', 'plugin.json'],
  ];

  for (const segments of candidates) {
    const manifestPath = path.join(pluginsDir, ...segments);

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Open install-state.json and confirm it starts with {.
  2. If empty, restore a minimal valid state object or back up and remove the file so ECC reinstalls cleanly.
  3. Confirm no external process is writing to the state file.

Example fix

// before (install-state.json)
[]

// after
{
  "schemaVersion": "ecc.install.v1",
  "target": {},
  "resolution": { "selectedModules": [] },
  "operations": []
}
Defensive patterns

Strategy: type-guard

Validate before calling

function preflightStateObject(p) {
  if (!fs.existsSync(p)) return;
  const v = JSON.parse(fs.readFileSync(p, 'utf8'));
  if (!(v !== null && typeof v === 'object' && !Array.isArray(v))) {
    throw new Error(`${p} top level must be a JSON object`);
  }
}
preflightStateObject(statePath);

Type guard

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

Prevention

When it happens

Trigger: An install-state.json whose top level is a JSON array, scalar, or null even though it parses cleanly.

Common situations: Another tool overwrote install-state.json with a list; a script wrote [] by accident; a generator emitted null for an empty state.

Related errors


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