{"record":{"id":"23ef61e6cfe203d7","repo":"affaan-m/ECC","slug":"policy-file-resolvedpath-must-define-labels-epic-as-a-non","errorCode":null,"errorMessage":"Policy file ${resolvedPath} must define labels.epic as a non-empty string","messagePattern":"Policy file (.+?) must define labels\\.epic as a non-empty string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/github-coordination/policy.js","lineNumber":79,"sourceCode":"\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 },\n    project: {\n      ...DEFAULT_POLICY.project,\n      ...project,\n      fieldNames: { ...DEFAULT_POLICY.project.fieldNames, ...fieldNames },\n    },\n    sourcePath: resolvedPath,\n  };\n}\n\nmodule.exports = {","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/github-coordination/policy.js#L61-L97","documentation":"loadPolicy requires the merged labels map (user labels overlaid on DEFAULT_LABELS) to contain labels.epic as a non-empty string, because the epic label is central to coordination workflows. It throws when labels.epic is missing, not a string, or an empty/whitespace-only string after merging with defaults.","triggerScenarios":"Calling loadPolicy with a policy file that sets labels to an object overriding epic with \"\", \"  \", null, or a non-string (e.g. a number or nested object), thereby clobbering the default epic label.","commonSituations":"Teams renaming labels and setting \"epic\": \"\" as a placeholder, renaming the key itself (e.g. \"epics\") so the epic key is lost, programmatic generation writing null for epic, or copy-pasting a partial labels block from another project.","solutions":["Set labels.epic to a non-empty label name in the policy file, e.g. \"epic\": \"epic\".","Ensure the key is spelled exactly 'epic' (not 'epics') inside labels.","Remove the labels override entirely to fall back to DEFAULT_LABELS.","If epic was intentionally renamed in GitHub, use the new real label name as the value (must exist in the repo)."],"exampleFix":"// before\n{ \"labels\": { \"epic\": \"\" } }\n// after\n{ \"labels\": { \"epic\": \"epic\" } }","handlingStrategy":"validation","validationCode":"const parsed = JSON.parse(fs.readFileSync(policyPath, 'utf8'));\nconst epic = (parsed.labels || {}).epic;\nif (typeof epic !== 'string' || !epic.trim()) throw new Error('policy.labels.epic must be a non-empty string');","typeGuard":"const hasValidEpicLabel = (p) => typeof p?.labels?.epic === 'string' && p.labels.epic.trim().length > 0;","tryCatchPattern":"try {\n  const policy = loadPolicy(p);\n} catch (e) {\n  if (e.message.includes('labels.epic')) {\n    console.error(`Set labels.epic to a non-empty string in ${p}, or remove the labels override to use defaults.`);\n  } else { throw e; }\n}","preventionTips":["Never set labels.epic to empty string/null as a placeholder.","Spell the key exactly 'epic'.","Omit the labels block entirely if you want defaults.","Validate policy files against the expected schema in CI."],"tags":["config","validation","labels"],"backgroundTag":"missing-required-config-field","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"}