heygen-com/hyperframes · error

Whisper did not produce output. Check the input file.

Error message

Whisper did not produce output. Check the input file.

What it means

After the whisper-cli child process exits (exit code 0), transcribe() expects a transcript JSON at `${outputDir}/transcript.json`. If existsSync() reports it missing, this error fires. It means whisper returned success but wrote no output file, almost always indicating the input audio was empty, corrupt, in an incompatible format, or too short for whisper to segment.

Source

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

      stdio: "ignore",
      timeout: whisperTimeoutMs,
    });
  } catch (err) {
    // Surface the timeout knob when the child was killed by our own timeout —
    // otherwise the reporter sees a bare ETIMEDOUT / SIGTERM with no hint that
    // `--timeout` even exists. Non-timeout errors flow through unchanged so the
    // existing stderr-tail handling in `transcribeAudio` still applies.
    throw wrapWhisperTimeoutError(err, {
      effectiveTimeoutMs: whisperTimeoutMs,
      model: effectiveModel,
      wasOverride: options?.timeoutMs != null,
    });
  }

  // 6. Read and validate output
  const transcriptPath = `${outputBase}.json`;
  if (!existsSync(transcriptPath)) {
    throw new Error("Whisper did not produce output. Check the input file.");
  }

  const transcript = JSON.parse(readFileSync(transcriptPath, "utf-8"));
  const segments = transcript.transcription ?? [];

  let wordCount = 0;
  let maxEnd = 0;
  for (const seg of segments) {
    for (const token of seg.tokens ?? []) {
      const text = (token.text ?? "").trim();
      if (text && !text.startsWith("[_") && !text.startsWith("[BLANK")) wordCount++;
      if (token.offsets?.to > maxEnd) maxEnd = token.offsets.to;
    }
  }

  // 7. Detect speech onset before cleaning up the WAV
  options?.onProgress?.("Detecting speech onset...");
  const speechOnsetSeconds = detectSpeechOnset(wavPath);

View on GitHub (pinned to c2996c8626)

Solutions

  1. Inspect the prepared WAV: play it or run `ffprobe ./prepared.wav` to confirm it has audio stream, a sane duration (>0), and 16kHz mono; re-source the input if it is empty/silent.
  2. Give each transcribe() call a unique outputDir so concurrent runs cannot clobber each other's transcript.json (the base name 'transcript' is hard-coded).
  3. Re-run the whisper step manually with the same args (printed in the failure) and read its stderr — whisper-cli often logs why it produced no segments even on exit 0.
  4. Verify the output directory is writable and has free disk space before the run.

Example fix

// before
await transcribe(silentOrEmptyClip, sharedOutputDir);

// after — guard duration and use a per-call output dir
import { statSync } from "node:fs";
const wav = prepareAudio(silentOrEmptyClip);
// ffprobe / inspect: if duration is ~0, the source has no usable audio
const out = join(tmpdir(), `tx-${randomUUID()}`);
await transcribe(wav, out);
Defensive patterns

Strategy: try-catch

Validate before calling

import { statSync, existsSync } from "node:fs";
// pre-check the prepared WAV is non-empty and the output dir is writable & unique
if (statSync(wavPath).size === 0) throw new Error('prepared WAV is empty — source has no audio');
const out = join(tmpdir(), `tx-${randomUUID()}`); // unique dir avoids collisions

Type guard

null

Try / catch

try {
  await transcribe(input, uniqueOutDir);
} catch (err) {
  if (/did not produce output/.test(String(err))) {
    // inspect the WAV with ffprobe; re-source silent/empty input; do not retry unchanged
  } else throw err;
}

Prevention

When it happens

Trigger: whisper-cli was invoked with a WAV that is 0 seconds long, contains only silence, has an unsupported sample rate/channel layout, or is a malformed WAV header; whisper exits 0 but emits nothing. Also possible if the output directory is on a read-only / full filesystem, or if two concurrent transcribe() calls collide on the same outputDir (transcript base name is fixed as 'transcript').

Common situations: A scene whose captured audio is silent (no narration was recorded); a video with no audio track at all, where extractAudio produced an empty WAV; parallel renders sharing one outputDir; a corrupt download used as input.

Related errors


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