{"record":{"id":"7ca692aa46445024","repo":"heygen-com/hyperframes","slug":"at-least-one-grade-cell-is-required","errorCode":null,"errorMessage":"At least one grade cell is required","messagePattern":"At least one grade cell is required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/commands/grade-compare.ts","lineNumber":322,"sourceCode":"} {\n  const columns = Math.max(1, Math.min(MAX_COLUMNS, Math.ceil(Math.sqrt(cellCount))));\n  const rows = Math.ceil(cellCount / columns);\n  const cellImageWidth = DEFAULT_CELL_WIDTH;\n  const aspect = frameHeight > 0 && frameWidth > 0 ? frameHeight / frameWidth : 9 / 16;\n  const cellImageHeight = Math.max(1, Math.round(cellImageWidth * aspect));\n  return {\n    columns,\n    rows,\n    cellImageWidth,\n    cellImageHeight,\n    width: columns * cellImageWidth + (columns + 1) * GRID_PADDING,\n    height: rows * (cellImageHeight + LABEL_HEIGHT) + (rows + 1) * GRID_PADDING,\n  };\n}\n\nexport function buildGradeCompareHtml(options: GradeCompareHtmlOptions): string {\n  if (options.cells.length === 0) {\n    throw new Error(\"At least one grade cell is required\");\n  }\n  const metrics = gridMetrics(options.cells.length, options.frameWidth, options.frameHeight);\n\n  const cellHtml = options.cells\n    .map((cell, index) => {\n      const serialized = escapeSingleQuotedAttr(serializedGradingForCell(cell));\n      const label = escapeXml(cell.label);\n      const row = Math.floor(index / metrics.columns);\n      const col = index % metrics.columns;\n      const left = GRID_PADDING + col * (metrics.cellImageWidth + GRID_PADDING);\n      const top = GRID_PADDING + row * (metrics.cellImageHeight + LABEL_HEIGHT + GRID_PADDING);\n      return `      <figure class=\"grade-cell\" style=\"left:${left}px;top:${top}px;width:${metrics.cellImageWidth}px;height:${metrics.cellImageHeight + LABEL_HEIGHT}px\">\n        <figcaption>${label}</figcaption>\n        <img src=\"${escapeXml(options.frameSrc)}\" ${HF_COLOR_GRADING_ATTR}='${serialized}' alt=\"${label}\" />\n      </figure>`;\n    })\n    .join(\"\\n\");\n","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/commands/grade-compare.ts#L304-L340","documentation":"Thrown by buildGradeCompareHtml when the cells array passed in is empty. This is a defensive guard on the HTML-builder API surface — the command layer already rejects empty candidate lists at line 638 (`At least one grade candidate is required`), so a programmatic caller hitting this has bypassed that upstream check. An empty grid has no layout to compute.","triggerScenarios":"Calling `buildGradeCompareHtml({ cells: [], frameSrc, frameWidth, frameHeight })` directly from a script or test without first ensuring cells is non-empty.","commonSituations":"Programmatic use of the exported builder; a test that constructs an empty cells array; a filter/map chain upstream that produced zero cells unexpectedly.","solutions":["Ensure the cells array has at least one GradeCompareCell before calling buildGradeCompareHtml.","If you reached this from the CLI, the upstream guard at grade-compare.ts:638 should have fired first — file a bug if you see this message from `hyperframes grade-compare`.","For tests, prepend a baseline cell via prependBaselineCell or pass a real candidate."],"exampleFix":"// before\nbuildGradeCompareHtml({ cells: [], frameSrc: 'frame.png', frameWidth: 1920, frameHeight: 1080 });\n// after\nbuildGradeCompareHtml({ cells: [{ label: 'original', grading: {} }], frameSrc: 'frame.png', frameWidth: 1920, frameHeight: 1080 });","handlingStrategy":"validation","validationCode":"// Before calling buildGradeCompareHtml programmatically\nif (options.cells.length === 0) {\n  throw new Error(\"cells must be non-empty before building HTML\");\n}\nconst html = buildGradeCompareHtml(options);","typeGuard":"function hasCells(cells: readonly unknown[]): cells is unknown[] {\n  return cells.length > 0;\n}","tryCatchPattern":"try {\n  const html = buildGradeCompareHtml(opts);\n} catch (err) {\n  if (/At least one grade cell/.test((err as Error).message)) {\n    // you bypassed the command-layer guard — supply a cell\n    opts.cells = [{ label: \"original\", grading: {} }];\n  }\n}","preventionTips":["Always prepend a baseline cell (prependBaselineCell) so the array is never empty.","Assert cells.length > 0 in tests that exercise buildGradeCompareHtml.","Treat this error as a bug if it appears from the CLI — the command layer should have caught it first."],"tags":["cli","input-validation","grade-compare","programmatic-api","defensive-guard"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}