heygen-com/hyperframes · error

Unsupported file type: ${ext}

Error message

Unsupported file type: ${ext}

What it means

Thrown by transcribe() when the input file's extension matches neither AUDIO_EXTENSIONS (.mp3/.wav/.m4a/.aac/.ogg/.flac) nor VIDEO_EXTENSIONS (.mp4/.webm/.mov/.mkv/.avi). The lowercased extension is interpolated into the message so you can see exactly what was rejected. This is a hard gate: only those two extension sets are accepted, decided by extname() alone (not by file content / MIME sniffing).

Source

Thrown at packages/cli/src/whisper/transcribe.ts:435

  });

  // 3. Prepare audio
  let wavPath: string;
  const ext = extname(inputPath).toLowerCase();

  if (isAudioFile(inputPath)) {
    options?.onProgress?.("Preparing audio...");
    wavPath = prepareAudio(inputPath);
  } else if (isVideoFile(inputPath)) {
    if (!hasFFmpeg()) {
      throw new Error(
        `ffmpeg is required to extract audio from video. Install: ${getFFmpegInstallHint()}`,
      );
    }
    options?.onProgress?.("Extracting audio from video...");
    wavPath = extractAudio(inputPath);
  } else {
    throw new Error(`Unsupported file type: ${ext}`);
  }

  // 4. Detect language and ensure correct model
  let effectiveModel = model;
  let effectiveModelPath = modelPath;
  let detectedLanguage = options?.language ?? null;

  // Only auto-detect language when using a multilingual model.
  // .en models always report "en" regardless of actual language, so detection
  // would be a no-op. If the user chose .en, they want English.
  if (!detectedLanguage && !effectiveModel.endsWith(".en")) {
    options?.onProgress?.("Detecting language...");
    detectedLanguage = detectLanguage(whisper.executablePath, effectiveModelPath, wavPath);
  }

  if (detectedLanguage && detectedLanguage !== "en" && effectiveModel.endsWith(".en")) {
    const multilingualModel = effectiveModel.replace(/\.en$/, "");
    options?.onProgress?.(

View on GitHub (pinned to c2996c8626)

Solutions

  1. Check the file extension against the accepted sets and convert the source to a supported format first (e.g. ffmpeg -i in.opus out.wav, or transcode video to .mp4).
  2. If the file genuinely is supported audio/video with a non-listed extension, rename or transcode it to one of the accepted extensions before calling transcribe().
  3. Confirm the path points at a file, not a directory — extname on a directory yields '' and falls through to this branch.
  4. Request/confirm whether your container should be added to AUDIO_EXTENSIONS or VIDEO_EXTENSIONS upstream if it is a common format HyperFrames should support.

Example fix

// before
await transcribe("./recording.opus", "./out"); // throws Unsupported file type: .opus

// after — transcode to a supported extension first
import { execFileSync } from "node:child_process";
execFileSync("ffmpeg", ["-y", "-i", "./recording.opus", "-ar", "16000", "-ac", "1", "./recording.wav"]);
await transcribe("./recording.wav", "./out");
Defensive patterns

Strategy: validation

Validate before calling

import { extname } from "node:path";
const AUDIO = new Set([".mp3",".wav",".m4a",".aac",".ogg",".flac"]);
const VIDEO = new Set([".mp4",".webm",".mov",".mkv",".avi"]);
function isTranscribable(p: string): boolean {
  const ext = extname(p).toLowerCase();
  return AUDIO.has(ext) || VIDEO.has(ext);
}
if (!isTranscribable(inputPath)) throw new Error(`unsupported extension ${extname(inputPath)}`);

Type guard

function isTranscribableFile(p: string): boolean {
  const ext = extname(p).toLowerCase();
  return AUDIO.has(ext) || VIDEO.has(ext);
}

Try / catch

try { await transcribe(p, out); }
catch (err) { if (/Unsupported file type/.test(String(err))) { /* convert source */ } else throw err; }

Prevention

When it happens

Trigger: Passing a file whose extname() is outside both sets — e.g. .opus, .wma, .aiff, .ts, .m2ts, .3gp, .gif, .bmp, a dotfile with no extension, or a path where the extension is upper-case-mangled after a manual toLowerCase bug. A path that is actually a directory also lands here because extname returns ''.

Common situations: User drags a .mov exported from a phone renamed to .MOV (covered by toLowerCase) but more often an unusual container like .opus audio or .m2ts video; pointing transcribe at a still image or a text file by mistake; a pipeline that auto-generates a filename with no extension.

Related errors


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