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.
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)
Solutions
- Ensure the file is inside the project directory and ends with .html
- Pass --project <dir> to set the correct project root if the file is legitimately in a sibling project
- Move or copy the composition into the project directory
- 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
- Always pass --project when the cwd is not the project root
- Use project-relative paths only; never absolute paths outside the project
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
- --selector-index must be a non-negative integer
- Media analysis requires a local project asset; freeze remote
- Use either --apply with --grading or --clear, not both
- --grading requires --apply
- --apply requires --grading <json>
AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12).
Data as JSON: /api/errors/bd13eea7f7e4c738.
Report an issue: GitHub.