{"record":{"id":"0bd6f4d5cd4fbcce","repo":"affaan-m/ECC","slug":"invalid-label-at-filepath-expected-a-json-object-plan","errorCode":null,"errorMessage":"Invalid ${label} at ${filePath}: expected a JSON object","messagePattern":"Invalid (.+?) at (.+?): expected a JSON object","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/plan.js","lineNumber":128,"sourceCode":"    sourceRelativePath,\n    destinationPath,\n    strategy,\n    ownership: 'managed',\n    scaffoldOnly: false,\n    ...(contentTransform ? { contentTransform } : {}),\n  };\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 materializeClaudeSettingsOperation(sourceRoot, operation) {\n  const sourcePath = path.join(sourceRoot, operation.sourceRelativePath);\n  if (!fs.existsSync(sourcePath)) {\n    return [];\n  }\n\n  // Stable ids and descriptions live in hooks/hooks.metadata.json; readHooksConfig\n  // merges them back so managed settings entries keep their ids.\n  const hooksConfig = readHooksConfig(sourcePath, operation.sourceRelativePath);\n  const managedHooks = materializeManagedHooks(\n    hooksConfig,\n    path.dirname(operation.destinationPath)\n  );","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/plan.js#L110-L146","documentation":"After readJsonObject successfully parses a JSON file, it validates the shape: the value must be a non-null object and not an array. If the file contains valid JSON whose top level is an array, a string, a number, a boolean, or null, this error is thrown. It distinguishes 'parsed but wrong shape' from 'unparseable' (error 363).","triggerScenarios":"Calling readJsonObject(filePath, label) when the file parses but its root value is `null`, an array (e.g. a top-level `[ ... ]` list), or a JSON scalar such as `\"text\"`, `42`, or `true` instead of an object.","commonSituations":"A scaffold file exported as a JSON array of items; a config file containing just `null` or an empty file's fallback; hand-written config using a bare string; tooling that serializes an array of components where an object map was expected.","solutions":["Wrap the top-level value in an object: change `[ ... ]` to `{ \"items\": [ ... ] }` or the expected object shape.","Replace `null`/scalar contents with the required object structure for the label shown in the message.","Regenerate the file with the tooling that produced the correct object schema.","Check documentation for the expected schema of the labeled file (e.g. scaffold/settings JSON object)."],"exampleFix":"// before: top-level array\n[\n  { \"id\": \"a\" },\n  { \"id\": \"b\" }\n]\n\n// after: top-level object\n{\n  \"components\": [\n    { \"id\": \"a\" },\n    { \"id\": \"b\" }\n  ]\n}","handlingStrategy":"type-guard","validationCode":"const v = JSON.parse(fs.readFileSync(p, 'utf8'));\nif (v === null || typeof v !== 'object' || Array.isArray(v)) {\n  throw new Error(`${p} 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 cfg = readJsonObject(path, label);\n} catch (e) {\n  if (e.message.includes('expected a JSON object')) {\n    console.error(`${path} has wrong top-level shape: wrap arrays/scalars in an object.`);\n    return regenerateFile(path);\n  }\n  throw e;\n}","preventionTips":["Match the documented schema: top-level object, never a bare array or scalar.","Add a shape assertion in tests for every config file the tooling reads.","Regenerate files with the official scaffolder instead of hand-writing structure.","Validate parsed JSON shape at ingestion boundaries in your own scripts."],"tags":["json","schema","type-mismatch"],"backgroundTag":"unexpected-response-shape","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"}