heygen-com/hyperframes · error · Error

--file must be an HTML file inside the project

Error message

--file must be an HTML file inside the project

What it means

Thrown by resolveMutationFile when the resolved --file path either falls outside the project directory (path traversal) or does not end with .html. This is a security boundary (isPathInside) combined with an extension guard ensuring only HTML composition files inside the project are mutated.

Solutions

  1. Ensure the file is inside the project directory and ends with .html
  2. Pass --project <dir> to set the correct project root if the file is legitimately in a sibling project
  3. Move or copy the composition into the project directory
  4. Check the extension is exactly .html (not .htm or .xhtml)

Example fix

// before
hyperframes media-treatment --file ../shared/scene.html --selector '#hero' --apply
// after — file inside the project
hyperframes media-treatment --file compositions/scene.html --selector '#hero' --apply
Defensive patterns

Strategy: validation

Validate before calling

import { resolve } from 'node:path';
import { isPathInside } from '@hyperframes/core';

function assertFileInsideProject(projectDir: string, fileArg: string): void {
  const filePath = resolve(projectDir, fileArg);
  if (!isPathInside(filePath, projectDir) || !filePath.toLowerCase().endsWith('.html')) {
    throw new Error('--file must be an HTML file inside the project');
  }
}

Prevention

When it happens

Trigger: Passing `--file ../outside.html` (escapes project dir), `--file /etc/passwd` (absolute outside project), or `--file scene.txt` (wrong extension). Also triggered by symlinks resolving outside the project root.

Common situations: Monorepo users pointing at a shared composition in a parent directory. Typos like --file index.htm. Agents guessing paths outside the resolved project root.

Related errors


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

Appendix: source

Thrown at packages/cli/src/commands/media-treatment.ts:577

    args.apply,
    args.analyze,
  ].some(Boolean);
  if (hasMutationOption) throw new Error("--capabilities cannot be combined with mutation options");
  if (args.all === true && capability)
    throw new Error("Use either --all or --capability, not both");

  let capabilities: unknown = getMediaTreatmentCapabilityOverview();
  if (args.all === true) capabilities = getHfColorGradingCapabilities();
  else if (capability) capabilities = getMediaTreatmentCapabilityDetail(capability);
  console.log(JSON.stringify(withMeta({ ok: true, capabilities }), null, 2));
}

function resolveMutationFile(args: MediaTreatmentCommandArgs) {
  const project = resolveProject(readOptionalString(args.project));
  const fileArg = readOptionalString(args.file) ?? "index.html";
  const filePath = resolve(project.dir, fileArg);
  if (!isPathInside(filePath, project.dir) || !filePath.toLowerCase().endsWith(".html")) {
    throw new Error("--file must be an HTML file inside the project");
  }
  if (!existsSync(filePath)) throw new Error(`Composition file not found: ${fileArg}`);
  return { project, filePath };
}

function analyzeTarget(args: MediaTreatmentCommandArgs) {
  const { project, filePath } = resolveMutationFile(args);
  const selector = readOptionalString(args.selector);
  if (!selector) throw new Error("--selector is required");
  if (
    readOptionalString(args.grading) ||
    args.clear === true ||
    args.apply === true ||
    args["dry-run"] === true
  ) {
    throw new Error("--analyze cannot be combined with mutation options");
  }
  const selectorIndex = parseSelectorIndex(readOptionalString(args["selector-index"]));

View on GitHub (pinned to c2996c8626)