{"record":{"id":"3dce563a1ccb6498","repo":"affaan-m/ECC","slug":"invalid-label-at-filepath-expected-a-json-o-3dce56","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-targets/cursor-project.js","lineNumber":32,"sourceCode":"  if (path.basename(sourceRelativeFile).toLowerCase() === 'readme.md') {\n    return null;\n  }\n\n  return fileName.endsWith('.md')\n    ? `${fileName.slice(0, -3)}.mdc`\n    : fileName;\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 createJsonMergeOperation({ moduleId, repoRoot, sourceRelativePath, destinationPath }) {\n  const sourcePath = path.join(repoRoot, sourceRelativePath);\n  if (!fs.existsSync(sourcePath) || !fs.statSync(sourcePath).isFile()) {\n    return null;\n  }\n\n  return createManagedOperation({\n    kind: 'merge-json',\n    moduleId,\n    sourceRelativePath,\n    destinationPath,\n    strategy: 'merge-json',\n    ownership: 'managed',","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install-targets/cursor-project.js#L14-L50","documentation":"After successfully parsing the file, readJsonObject in the Cursor project target additionally requires the top-level value to be a plain JSON object — not null, not an array. Config files this installer merges into must be objects so keys can be merged; anything else is rejected with the file path in the message.","triggerScenarios":"createJsonMergeOperation reads a destination file whose contents parse as JSON but whose root is an array (e.g. `[]` or `[...]`), a bare string/number, or the literal `null`.","commonSituations":"Someone reset a Cursor config file to `[]` assuming it is a list format; a tool exported settings as a JSON array; a script wrote `null` as a placeholder.","solutions":["Open the file at filePath in the message and change the top-level value to a JSON object `{ ... }`","If the file is an empty placeholder, replace `[]` or `null` with `{}`","Re-run the install so the merge operation can apply on the now-object-shaped file"],"exampleFix":"// before\n[]\n// after\n{}","handlingStrategy":"type-guard","validationCode":"// Pre-check the root shape before install\nconst parsed = JSON.parse(fs.readFileSync(cursorConfigPath, 'utf8'));\nif (parsed === null || Array.isArray(parsed) || typeof parsed !== 'object') {\n  console.error(`${cursorConfigPath} must be a JSON object at the top level`);\n}","typeGuard":"function isJsonObject(v) {\n  return v !== null && typeof v === 'object' && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  runInstallOperation(op);\n} catch (e) {\n  if (/Invalid .* at .*: expected a JSON object/.test(e.message)) {\n    // rewrite the file's root as an object and retry\n  } else throw e;\n}","preventionTips":["Never replace object-shaped config files with arrays or null placeholders","Use `{}` as the empty-state file content, not `[]` or `null`","Validate file root shape with a type guard before invoking merge operations"],"tags":["json","type-mismatch","cursor","install-target"],"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"}