{"record":{"id":"9de5c7995fc50087","repo":"affaan-m/ECC","slug":"invalid-label-at-filepath-expected-a-json-o-9de5c7","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/apply.js","lineNumber":84,"sourceCode":"        destRel: path.relative(plan.targetRoot, operation.destinationPath),\n      });\n    }\n  }\n  return buildInstallIndex(mappings);\n}\n\nfunction readJsonObject(filePath, label) {\n  let parsed;\n  try {\n    parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));\n  } catch (error) {\n    const wrappedError = new Error(`Failed to parse ${label} at ${filePath}: ${error.message}`);\n    wrappedError.code = error.code;\n    throw wrappedError;\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 readOptionalJsonObject(filePath, label) {\n  try {\n    return readJsonObject(filePath, label);\n  } catch (error) {\n    if (error.code === 'ENOENT') {\n      return {};\n    }\n    throw error;\n  }\n}\n\nfunction readInstalledFileNoFollow(plan, operation) {\n  assertSafeInstallOperation(plan, operation);","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/apply.js#L66-L102","documentation":"readJsonObject parses a JSON file and requires the result to be a non-null, non-array plain object, because installer config files (like .mcp.json or settings JSON) must be key/value maps to merge safely. Nulls, arrays, and scalars are rejected with a message naming the file's label and path. A parse failure itself throws a separate wrapped error; this one is purely about the parsed shape.","triggerScenarios":"readJsonObject(filePath, label) reading a file whose top-level JSON is an array ('[...]'), a string/number/boolean literal, or literal 'null' — e.g. a settings file saved as '[]' or a config overwritten with '\"text\"'.","commonSituations":"A .mcp.json or settings file that was reset to an empty array; a user replacing an object config with a list of servers; a script serializing an array where an object map was expected; hand-edited JSON losing its root braces.","solutions":["Edit the file so its top level is a JSON object: wrap contents in { } and use key/value pairs.","If the file is an empty array, replace it with {}.","Validate with JSON.parse plus a typeof check before handing the path to the installer.","Check which tool rewrote the file and fix its serialization to emit an object."],"exampleFix":"// before: .mcp.json\n[]\n// after\n{\n  \"mcpServers\": {}\n}","handlingStrategy":"validation","validationCode":"const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));\nif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n  throw new Error(`${filePath} must contain a top-level JSON object`);\n}","typeGuard":"const isJsonObject = (v) => Boolean(v) && typeof v === 'object' && !Array.isArray(v);","tryCatchPattern":"try {\n  const cfg = readJsonObject(p, 'MCP config');\n} catch (e) {\n  if (e.message.includes('expected a JSON object')) {\n    console.error(`${p}: rewrite the file with a top-level { } object.`);\n  }\n  throw e;\n}","preventionTips":["Validate config JSON shape (object, not array/scalar) after any tool rewrites it.","Reset broken configs to {} rather than [] when clearing them.","Add a JSON schema check for .mcp.json / settings files in CI or a pre-install lint."],"tags":["json","config","validation"],"backgroundTag":"invalid-json-response","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"}