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
- Open install-state.json and confirm it starts with {.
- If empty, restore a minimal valid state object or back up and remove the file so ECC reinstalls cleanly.
- 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
- Treat install-state.json as opaque; never write to it directly.
- Audit external tools that may touch the file to confirm they emit objects.
- Use a minimal valid object ({ schemaVersion, target, resolution, operations }) if you must seed one.
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
- ${label} is invalid at ${filePath}: ${error.message}
- Failed to read ${label}: ${error.message}
- Invalid install-state${label ? ` (${label})` : ''}: ${format
- Invalid ${label} at ${filePath}: expected a JSON object
- Invalid ${label} at ${filePath}: expected a JSON object
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/c9d15036aedc9175.
Report an issue: GitHub.