heygen-com/hyperframes · error

Unsupported input: ${ext}. Use a video (mp4/mov/webm/mkv/avi

Error message

Unsupported input: ${ext}. Use a video (mp4/mov/webm/mkv/avi) or image (jpg/png/webp).

What it means

Thrown by inferInputKind() when the input file's extension matches neither VIDEO_EXTENSIONS (.mp4/.mov/.webm/.mkv/.avi) nor IMAGE_EXTENSIONS (.jpg/.jpeg/.png/.webp). The background-removal pipeline only accepts raster video or image inputs because it feeds raw RGB24 frames to an ONNX inference session. Any other extension (e.g. .gif, .tiff, .bmp, no extension, or a typo) is rejected before any ffmpeg probe runs.

Source

Thrown at packages/cli/src/background-removal/pipeline.ts:100

  fps: number;
  frameCount: number;
}

export function inferOutputFormat(outputPath: string): OutputFormat {
  const ext = extname(outputPath).toLowerCase();
  if (ext === ".webm") return "webm";
  if (ext === ".mov") return "mov";
  if (ext === ".png") return "png";
  throw new Error(
    `Unsupported output extension: ${ext}. Use .webm (VP9 alpha), .mov (ProRes 4444), or .png.`,
  );
}

export function inferInputKind(inputPath: string): "video" | "image" {
  const ext = extname(inputPath).toLowerCase();
  if (VIDEO_EXTENSIONS.has(ext)) return "video";
  if (IMAGE_EXTENSIONS.has(ext)) return "image";
  throw new Error(
    `Unsupported input: ${ext}. Use a video (mp4/mov/webm/mkv/avi) or image (jpg/png/webp).`,
  );
}

interface EngineMetadata {
  width: number;
  height: number;
  fps: number;
  durationSeconds: number;
}

async function probeMedia(inputPath: string): Promise<MediaInfo> {
  const isImage = inferInputKind(inputPath) === "image";
  const engine = (await import("@hyperframes/engine")) as {
    extractMediaMetadata: (path: string) => Promise<EngineMetadata>;
  };
  const meta = await engine.extractMediaMetadata(inputPath);

View on GitHub (pinned to c2996c8626)

Solutions

  1. Convert the input to a supported format: video → .mp4/.mov/.webm, still → .png/.jpg/.webp (ffmpeg or an image tool).
  2. Check the file extension is spelled correctly and present: run `ls -la <inputPath>` and confirm the ext.
  3. If you genuinely need .gif, extract frames first: `ffmpeg -i in.gif -vsync 0 frame%04d.png` then pass a frame, or convert to mp4.

Example fix

// before
await render({ inputPath: 'clip.gif', outputPath: 'out.webm' });
// after (convert first)
// $ ffmpeg -i clip.gif -movflags faststart clip.mp4
await render({ inputPath: 'clip.mp4', outputPath: 'out.webm' });
Defensive patterns

Strategy: validation

Validate before calling

import { extname } from 'node:path';
const VIDEO = new Set(['.mp4','.mov','.webm','.mkv','.avi']);
const IMAGE = new Set(['.jpg','.jpeg','.png','.webp']);
function assertSupportedInput(p: string) {
  const ext = extname(p).toLowerCase();
  if (!VIDEO.has(ext) && !IMAGE.has(ext)) {
    throw new Error(`Unsupported input extension ${ext}. Convert to mp4/mov/webm/mkv/avi (video) or jpg/png/webp (image).`);
  }
}
assertSupportedInput(options.inputPath);
await render(options);

Type guard

import { extname } from 'node:path';
function isSupportedInput(p: string): boolean {
  const ext = extname(p).toLowerCase();
  return new Set(['.mp4','.mov','.webm','.mkv','.avi','.jpg','.jpeg','.png','.webp']).has(ext);
}

Prevention

When it happens

Trigger: Calling render() or resolveRenderTargets() with an inputPath whose extname is outside the two extension sets. Examples: input '/clip.gif', input 'photo.tiff', input 'frame' (no ext), or a typo like 'video.mp4' written as 'video.mp4 ' with a trailing space.

Common situations: User passes a .gif or .tiff thinking it is a supported image; a glob or shell expansion produced an unexpected file (e.g. a .txt sidecar); the path lost its extension during a rename; a case mismatch where the file is actually .MP4 but extname returns it correctly after toLowerCase (works) — real failures are unsupported formats or missing extensions.

Related errors


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