{"record":{"id":"c9d15036aedc9175","repo":"affaan-m/ECC","slug":"label-is-invalid-at-filepath-expected-a-jso","errorCode":null,"errorMessage":"${label} is invalid at ${filePath}: expected a JSON object","messagePattern":"(.+?) is invalid at (.+?): expected a JSON object","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/inventory.js","lineNumber":39,"sourceCode":"    || path.join(homeDir, '.claude');\n  const projectRoot = options.projectRoot || process.cwd();\n\n  return {\n    homeDir: path.resolve(homeDir),\n    configDir: path.resolve(configDir),\n    projectRoot: path.resolve(projectRoot),\n  };\n}\n\nfunction readJsonObject(filePath, label) {\n  let value;\n  try {\n    value = JSON.parse(fs.readFileSync(filePath, 'utf8'));\n  } catch (error) {\n    throw new Error(`${label} is invalid at ${filePath}: ${error.message}`);\n  }\n  if (!value || typeof value !== 'object' || Array.isArray(value)) {\n    throw new Error(`${label} is invalid at ${filePath}: expected a JSON object`);\n  }\n  return value;\n}\n\nfunction findManualClaudePlugin(options = {}) {\n  const { configDir } = resolveClaudePaths(options);\n  const pluginsDir = path.join(configDir, 'plugins');\n  const candidates = [\n    ['ecc', '.claude-plugin', 'plugin.json'],\n    ['ecc', 'plugin.json'],\n    ['ecc@ecc', '.claude-plugin', 'plugin.json'],\n    ['ecc@ecc', 'plugin.json'],\n    ['everything-claude-code', '.claude-plugin', 'plugin.json'],\n    ['everything-claude-code', 'plugin.json'],\n  ];\n\n  for (const segments of candidates) {\n    const manifestPath = path.join(pluginsDir, ...segments);","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/inventory.js#L21-L57","documentation":"readJsonObject() additionally enforces that the parsed value is a plain JSON object. If the file parses to null, a primitive, or an array, it throws this error stating a JSON object was expected. It guards inventory/state readers against structurally wrong files that are nonetheless valid JSON.","triggerScenarios":"Calling readJsonObject(filePath, label) on a file whose top level is an array (e.g. a bare JSON list of modules), a string/number, or the literal `null`; pointing the reader at a different file that happens to be valid JSON but not an object.","commonSituations":"Someone 'simplifying' state files to an array; exporting a list from another tool into the state path; an empty-file edge where content like `[]` or `\"...\"` replaced the object; wrong file passed as the state/inventory path.","solutions":["Wrap the file's contents in a JSON object — e.g. change `[\"agents\"]` to `{ \"selectedModules\": [\"agents\"] }` matching the expected schema.","Verify you are pointing at the correct file (the label in the message tells you which document was expected).","Restore the original object-shaped file from git or regenerate it by rerunning the install flow.","If generating this file in tooling, ensure you serialize an object, never an array or scalar, at the top level."],"exampleFix":"// before (state.json)\n[\"agents\", \"skills\"]\n// after\n{ \"selectedModules\": [\"agents\", \"skills\"], \"operations\": [] }","handlingStrategy":"type-guard","validationCode":"const value = JSON.parse(fs.readFileSync(statePath, 'utf8'));\nif (value === null || typeof value !== 'object' || Array.isArray(value)) {\n  throw new Error(`${statePath} must contain a top-level JSON object`);\n}","typeGuard":"function isJsonObject(v) {\n  return v !== null && typeof v === 'object' && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  const state = readJsonObject(statePath, 'Managed Claude install-state');\n} catch (err) {\n  if (err.message.includes('expected a JSON object')) {\n    console.error(`${statePath} has the wrong top-level type; wrap contents in an object`);\n    process.exitCode = 1;\n  } else throw err;\n}","preventionTips":["Always serialize objects (never bare arrays or scalars) when writing state/inventory files.","Point tooling at the exact expected file paths instead of guessing between similar JSON files.","Add a schema/type check to any script that generates these files.","Document the expected top-level shape next to the file-producing code."],"tags":["json","type-mismatch","inventory"],"backgroundTag":"config-type-mismatch","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}