{"record":{"id":"91ddf37dfa22351f","repo":"affaan-m/ECC","slug":"invalid-label-at-filepath-expected-a-json-o","errorCode":null,"errorMessage":"Invalid ${label} at ${filePath}: expected a JSON object","messagePattern":"Invalid (.+?) at (.+?): expected a JSON object","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/install-executor.js","lineNumber":215,"sourceCode":"      sourceRelativePath: options.sourceRelativePath,\n      destinationPath: options.destinationPath,\n      strategy: options.strategy || 'preserve-relative-path'\n    })\n  );\n\n  return true;\n}\n\nfunction readJsonObject(filePath, label) {\n  let parsed;\n  try {\n    parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));\n  } catch (error) {\n    throw new Error(`Failed to parse ${label} at ${filePath}: ${error.message}`);\n  }\n\n  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n    throw new Error(`Invalid ${label} at ${filePath}: expected a JSON object`);\n  }\n\n  return parsed;\n}\n\nfunction addCursorAgentDataScaffoldOperations(operations, options) {\n  const scaffoldRoot = path.join(options.sourceRoot, 'scaffolds', 'cursor');\n  if (!fs.existsSync(scaffoldRoot)) {\n    return;\n  }\n\n  addFileCopyOperation(operations, {\n    moduleId: options.moduleId,\n    sourceRoot: options.sourceRoot,\n    sourceRelativePath: path.join('scaffolds', 'cursor', 'ecc-agent-data.json'),\n    destinationPath: path.join(options.targetRoot, 'ecc-agent-data.json'),\n    strategy: 'preserve-relative-path'\n  });","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/affaan-m/ECC/blob/06c5e118c4d3e6c3b7f9445f973a2194c82de193/scripts/lib/install-executor.js#L197-L233","documentation":"After a successful JSON.parse in readJsonObject(), the top-level value must be a plain JSON object. This error fires when the parsed root is anything else - an array, string, number, boolean or null - because downstream merge operations (deepMergeJson) need an object root to merge into.","triggerScenarios":"A hooks/MCP config whose top level is a JSON array (e.g. a bare list of servers); a payload file containing just a quoted string or a number; a file containing null.","commonSituations":"Users replacing an object-shaped config with a list; scaffolding or examples that emit arrays; misunderstanding of the expected config schema.","solutions":["Make the top-level value an object: wrap the array under a named key","Compare against the object shape in ECC's shipped example configs","Re-run the install after fixing; the merge will then apply key-by-key"],"exampleFix":"// before (top-level array)\n[ { \"matcher\": \"Bash\" } ]\n\n// after\n{ \"hooks\": { \"PreToolUse\": [ { \"matcher\": \"Bash\" } ] } }","handlingStrategy":"validation","validationCode":"const value = JSON.parse(fs.readFileSync(payloadPath, 'utf8'));\nif (value === null || typeof value !== 'object' || Array.isArray(value)) {\n  throw new Error(`${payloadPath}: top-level value must be a JSON object`);\n}","typeGuard":"function isJsonObject(value) {\n  return value !== null && typeof value === 'object' && !Array.isArray(value);\n}","tryCatchPattern":"try {\n  resolveInstallPlan(options);\n} catch (error) {\n  if (/expected a JSON object/.test(error.message)) {\n    // the named file parses but has a non-object root; wrap arrays under a key\n  } else {\n    throw error;\n  }\n}","preventionTips":["Model payload files with a JSON schema (e.g. ajv) and validate before install","Copy structural shapes from ECC's shipped example configs instead of inventing list forms"],"tags":["installer","json","type-validation","merge-payload"],"backgroundTag":"json-type-mismatch","analyzedSha":"06c5e118c4d3e6c3b7f9445f973a2194c82de193","analyzedAt":"2026-08-18T11:27:13.915Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}