{"record":{"id":"64fb28e08692a360","repo":"heygen-com/hyperframes","slug":"at-least-one-grade-candidate-is-required","errorCode":null,"errorMessage":"At least one grade candidate is required","messagePattern":"At least one grade candidate is required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/commands/grade-compare.ts","lineNumber":638,"sourceCode":"  async run({ args }) {\n    const jsonRequested = args.json === true;\n    let preparedDir: string | null = null;\n    try {\n      const parsed = parseGradeCompareArgs({\n        for: args.for,\n        grades: args.grades,\n        luts: args.luts,\n        project: args.project,\n        out: args.out,\n        json: args.json,\n        timeout: args.timeout,\n      });\n      let cells =\n        parsed.source.kind === \"grades\"\n          ? parseGradesFile(parsed.source.path)\n          : resolveLutCells(parsed.source.value);\n      if (cells.length === 0) {\n        throw new Error(\"At least one grade candidate is required\");\n      }\n      const capResult = capCandidateCells(cells);\n      cells = capResult.cells;\n      warnInactiveGradingCells(cells);\n      if (args.baseline !== false) {\n        cells = prependBaselineCell(cells);\n      }\n\n      if (!parsed.json) {\n        console.log(\n          `${c.accent(\"◆\")}  Rendering ${cells.length} grade candidates from ${c.accent(basename(parsed.framePath))}`,\n        );\n      }\n\n      const frame = await loadReferenceFrame(parsed.framePath);\n      const prepared = await prepareGradeCompareTempProject({\n        projectDir: parsed.projectDir,\n        framePath: parsed.framePath,","sourceCodeStart":620,"sourceCodeEnd":656,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/commands/grade-compare.ts#L620-L656","documentation":"Thrown by the grade-compare command's run() after parsing grades/luts and getting back an empty cells array. This is distinct from the lower-level buildGradeCompareHtml guard (error 144): it fires at the command layer, after parseGradesFile or resolveLutCells returned `[]`. In practice parseGradesFile cannot return empty (a JSON `[]` array passes the Array.isArray check and the map yields nothing), and resolveLutCells rejects empty input — so this is the guard that catches an empty `[]` grades file.","triggerScenarios":"Supplying `--grades grades.json` where grades.json is exactly `[]` (a valid JSON array with zero entries). The file parses, passes the array check, the map produces no cells, and this guard rejects it.","commonSituations":"A grades JSON templated/generated by a script that emitted an empty array when no looks matched a filter; hand-editing that stripped all entries; a CI step producing an empty manifest.","solutions":["Add at least one `{ label, grading }` entry to grades.json.","If a script generates the file, ensure it errors upstream when it would emit zero candidates rather than writing `[]`.","Switch to `--luts` with at least one .cube path if you have no inline grades to define."],"exampleFix":"// before — grades.json\n[]\n// after\n[{\"label\":\"Warm\",\"grading\":{\"temperature\":0.2}}]","handlingStrategy":"validation","validationCode":"// Reject an empty grades array before invoking grade-compare\nimport { readFileSync } from \"node:fs\";\nfunction assertNonEmptyGrades(path: string): void {\n  const parsed = JSON.parse(readFileSync(path, \"utf-8\"));\n  if (!Array.isArray(parsed) || parsed.length === 0) {\n    throw new Error(\"grades file must be a non-empty array\");\n  }\n}","typeGuard":"function isNonEmptyArray(v: unknown): v is unknown[] {\n  return Array.isArray(v) && v.length > 0;\n}","tryCatchPattern":"try {\n  // run grade-compare\n} catch (err) {\n  if (/At least one grade candidate/.test((err as Error).message))) {\n    // add an entry to grades.json or switch to --luts\n  }\n}","preventionTips":["Make any script that generates grades.json fail loudly when it would emit zero candidates.","Use isNonEmptyArray on the parsed file in a pre-flight check.","Prefer --luts for quick one-off comparisons to avoid empty-manifest edge cases."],"tags":["cli","input-validation","grade-compare","json","empty-input"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}