heygen-com/hyperframes · error · Error

At least one grade cell is required

Error message

At least one grade cell is required

What it means

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.

Source

Thrown at packages/cli/src/commands/grade-compare.ts:322

} {
  const columns = Math.max(1, Math.min(MAX_COLUMNS, Math.ceil(Math.sqrt(cellCount))));
  const rows = Math.ceil(cellCount / columns);
  const cellImageWidth = DEFAULT_CELL_WIDTH;
  const aspect = frameHeight > 0 && frameWidth > 0 ? frameHeight / frameWidth : 9 / 16;
  const cellImageHeight = Math.max(1, Math.round(cellImageWidth * aspect));
  return {
    columns,
    rows,
    cellImageWidth,
    cellImageHeight,
    width: columns * cellImageWidth + (columns + 1) * GRID_PADDING,
    height: rows * (cellImageHeight + LABEL_HEIGHT) + (rows + 1) * GRID_PADDING,
  };
}

export function buildGradeCompareHtml(options: GradeCompareHtmlOptions): string {
  if (options.cells.length === 0) {
    throw new Error("At least one grade cell is required");
  }
  const metrics = gridMetrics(options.cells.length, options.frameWidth, options.frameHeight);

  const cellHtml = options.cells
    .map((cell, index) => {
      const serialized = escapeSingleQuotedAttr(serializedGradingForCell(cell));
      const label = escapeXml(cell.label);
      const row = Math.floor(index / metrics.columns);
      const col = index % metrics.columns;
      const left = GRID_PADDING + col * (metrics.cellImageWidth + GRID_PADDING);
      const top = GRID_PADDING + row * (metrics.cellImageHeight + LABEL_HEIGHT + GRID_PADDING);
      return `      <figure class="grade-cell" style="left:${left}px;top:${top}px;width:${metrics.cellImageWidth}px;height:${metrics.cellImageHeight + LABEL_HEIGHT}px">
        <figcaption>${label}</figcaption>
        <img src="${escapeXml(options.frameSrc)}" ${HF_COLOR_GRADING_ATTR}='${serialized}' alt="${label}" />
      </figure>`;
    })
    .join("\n");

View on GitHub (pinned to c2996c8626)

Solutions

  1. Ensure the cells array has at least one GradeCompareCell before calling buildGradeCompareHtml.
  2. 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`.
  3. For tests, prepend a baseline cell via prependBaselineCell or pass a real candidate.

Example fix

// before
buildGradeCompareHtml({ cells: [], frameSrc: 'frame.png', frameWidth: 1920, frameHeight: 1080 });
// after
buildGradeCompareHtml({ cells: [{ label: 'original', grading: {} }], frameSrc: 'frame.png', frameWidth: 1920, frameHeight: 1080 });
Defensive patterns

Strategy: validation

Validate before calling

// Before calling buildGradeCompareHtml programmatically
if (options.cells.length === 0) {
  throw new Error("cells must be non-empty before building HTML");
}
const html = buildGradeCompareHtml(options);

Type guard

function hasCells(cells: readonly unknown[]): cells is unknown[] {
  return cells.length > 0;
}

Try / catch

try {
  const html = buildGradeCompareHtml(opts);
} catch (err) {
  if (/At least one grade cell/.test((err as Error).message)) {
    // you bypassed the command-layer guard — supply a cell
    opts.cells = [{ label: "original", grading: {} }];
  }
}

Prevention

When it happens

Trigger: Calling `buildGradeCompareHtml({ cells: [], frameSrc, frameWidth, frameHeight })` directly from a script or test without first ensuring cells is non-empty.

Common situations: Programmatic use of the exported builder; a test that constructs an empty cells array; a filter/map chain upstream that produced zero cells unexpectedly.

Related errors


AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12). Data as JSON: /api/errors/7ca692aa46445024. Report an issue: GitHub.