{"record":{"id":"bbe3e0dd1c646d88","repo":"affaan-m/ECC","slug":"policy-file-resolvedpath-must-contain-a-json-ob","errorCode":null,"errorMessage":"Policy file ${resolvedPath} must contain a JSON object, got ${Array.isArray(parsed) ? 'array' : typeof parsed}","messagePattern":"Policy file (.+?) must contain a JSON object, got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/github-coordination/policy.js","lineNumber":69,"sourceCode":"  const resolvedPath = configPath\n    ? path.resolve(configPath)\n    : path.join(rootDir, 'config', DEFAULT_CONFIG_FILE);\n\n  if (!fs.existsSync(resolvedPath)) {\n    return {\n      ...DEFAULT_POLICY,\n      sourcePath: null,\n    };\n  }\n\n  let parsed;\n  try {\n    parsed = JSON.parse(fs.readFileSync(resolvedPath, 'utf8'));\n  } catch (error) {\n    throw new Error(`Failed to load policy from ${resolvedPath}: ${error.message}`);\n  }\n  if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n    throw new Error(`Policy file ${resolvedPath} must contain a JSON object, got ${Array.isArray(parsed) ? 'array' : typeof parsed}`);\n  }\n  const labels = typeof parsed.labels === 'object' && parsed.labels !== null && !Array.isArray(parsed.labels) ? parsed.labels : {};\n  const review = typeof parsed.review === 'object' && parsed.review !== null && !Array.isArray(parsed.review) ? parsed.review : {};\n  const validation = typeof parsed.validation === 'object' && parsed.validation !== null && !Array.isArray(parsed.validation) ? parsed.validation : {};\n  const branchModel = typeof parsed.branchModel === 'object' && parsed.branchModel !== null && !Array.isArray(parsed.branchModel) ? parsed.branchModel : {};\n  const project = typeof parsed.project === 'object' && parsed.project !== null && !Array.isArray(parsed.project) ? parsed.project : {};\n  const fieldNames = typeof project.fieldNames === 'object' && project.fieldNames !== null && !Array.isArray(project.fieldNames) ? project.fieldNames : {};\n  const mergedLabels = { ...DEFAULT_LABELS, ...labels };\n  if (typeof mergedLabels.epic !== 'string' || !mergedLabels.epic.trim()) {\n    throw new Error(`Policy file ${resolvedPath} must define labels.epic as a non-empty string`);\n  }\n  return {\n    ...DEFAULT_POLICY,\n    ...parsed,\n    labels: mergedLabels,\n    review: { ...DEFAULT_POLICY.review, ...review },\n    validation: { ...DEFAULT_POLICY.validation, ...validation },\n    branchModel: { ...DEFAULT_POLICY.branchModel, ...branchModel },","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/github-coordination/policy.js#L51-L87","documentation":"loadPolicy throws when the parsed policy file is valid JSON but not a plain JSON object — i.e. it is null, an array, or a primitive (string/number/boolean). The policy schema requires an object whose keys (labels, review, validation, branchModel, project) are merged over DEFAULT_POLICY, so any other top-level type is unusable.","triggerScenarios":"Calling loadPolicy with a policy file whose top-level value is an array (e.g. [ {...} ]), a bare string/number, or the literal null — often from exporting the wrong structure or wrapping the object in an array.","commonSituations":"Someone exported Array.of(settings) or JSON.stringify of a list, a YAML-to-JSON conversion produced a list, an edit replaced the object with a scalar placeholder, or a template file was left as `null`.","solutions":["Open the policy file and ensure the top level is a JSON object: { \"labels\": {...}, ... }.","Remove any array wrapper or replace scalar/null placeholder content with the expected object.","Compare against the library's DEFAULT_POLICY shape or an existing valid policy file in the repo.","Validate with a quick script: JSON.parse(content) must satisfy typeof parsed === 'object' && !Array.isArray(parsed) && parsed !== null."],"exampleFix":"// before\n[{ \"labels\": { \"epic\": \"epic\" } }]\n// after\n{ \"labels\": { \"epic\": \"epic\" } }","handlingStrategy":"type-guard","validationCode":"const parsed = JSON.parse(fs.readFileSync(policyPath, 'utf8'));\nconst isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);\nif (!isPlainObject(parsed)) throw new Error(`Policy must be a JSON object, got ${Array.isArray(parsed) ? 'array' : typeof parsed}`);","typeGuard":"const isPolicyObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v) && typeof v.labels === 'object';","tryCatchPattern":"try {\n  const policy = loadPolicy(p);\n} catch (e) {\n  if (e.message.includes('must contain a JSON object')) {\n    console.error(`Rewrite ${p} so its top level is an object, not ${/got (.+)$/.exec(e.message)[1]}.`);\n  } else { throw e; }\n}","preventionTips":["Always export the policy as a single object, never an array or scalar.","Add a schema check (zod/ajv) in CI for policy files.","Start from a known-good policy template.","Spot-check generated policies with JSON.parse before committing."],"tags":["config","schema","json"],"backgroundTag":"schema-validation-failed","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"}