heygen-com/hyperframes · error · Error

Exactly one of --grades or --luts is required

Error message

Exactly one of --grades or --luts is required

What it means

Thrown by parseGradeCompareArgs when `--grades` and `--luts` are either both supplied or both absent (`Boolean(gradesArg) === Boolean(lutsArg)` covers both true and both false). The command needs exactly one source of candidate looks — a grades JSON file or a comma-separated LUT list — because mixing them would be ambiguous and supplying neither leaves nothing to render.

Source

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

  return [validateCell("original", {}), ...cells];
}

export function parseGradeCompareArgs(args: {
  for?: unknown;
  grades?: unknown;
  luts?: unknown;
  project?: unknown;
  out?: unknown;
  json?: unknown;
  timeout?: unknown;
}): ParsedGradeCompareArgs {
  const frameArg = readOptionalString(args.for);
  if (!frameArg) throw new Error("--for <path> is required");

  const gradesArg = readOptionalString(args.grades);
  const lutsArg = readOptionalString(args.luts);
  if (Boolean(gradesArg) === Boolean(lutsArg)) {
    throw new Error("Exactly one of --grades or --luts is required");
  }

  const projectDir = resolve(readOptionalString(args.project) ?? process.cwd());
  const framePath = resolveFromBase(projectDir, frameArg);
  const outPath = resolveFromBase(projectDir, readOptionalString(args.out) ?? "grade-compare.png");

  return {
    framePath,
    projectDir,
    outPath,
    source: gradesArg
      ? { kind: "grades", path: resolveFromBase(projectDir, gradesArg) }
      : { kind: "luts", value: lutsArg ?? "" },
    json: args.json === true,
    timeoutMs: Number.parseInt(readOptionalString(args.timeout) ?? "", 10) || 5000,
  };
}

View on GitHub (pinned to c2996c8626)

Solutions

  1. Pick one source: use `--grades grades.json` for inline-defined looks, or `--luts a.cube,b.cube` for .cube files.
  2. Remove whichever flag is unwanted from the command line.
  3. If neither is set, add the one that matches your asset type.

Example fix

# before (both)
hyperframes grade-compare --for f.png --grades g.json --luts a.cube
# after (pick one)
hyperframes grade-compare --for f.png --grades g.json
Defensive patterns

Strategy: validation

Validate before calling

// Ensure exactly one source flag is set
function pickSource(grades?: string, luts?: string): "grades" | "luts" {
  const g = Boolean(grades), l = Boolean(luts);
  if (g === l) throw new Error("Pass exactly one of --grades or --luts");
  return g ? "grades" : "luts";
}

Type guard

function exactlyOneSet(a: unknown, b: unknown): boolean {
  return Boolean(a) !== Boolean(b);
}

Try / catch

try {
  const parsed = parseGradeCompareArgs(args);
} catch (err) {
  if (/Exactly one of/.test((err as Error).message)) {
    console.error("Choose either --grades <file> or --luts <list>, not both/neither.");
  }
  throw err;
}

Prevention

When it happens

Trigger: Running grade-compare with both `--grades grades.json --luts a.cube`; or with neither flag; or with both empty strings.

Common situations: Copy-pasting a command that evolved from one mode to the other; CI templates that conditionally set both; forgetting which mode you intended.

Related errors


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