{"record":{"id":"caeb8a52b9bee091","repo":"heygen-com/hyperframes","slug":"whisper-did-not-produce-output-check-the-input-fi","errorCode":null,"errorMessage":"Whisper did not produce output. Check the input file.","messagePattern":"Whisper did not produce output\\. Check the input file\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/whisper/transcribe.ts","lineNumber":506,"sourceCode":"      stdio: \"ignore\",\n      timeout: whisperTimeoutMs,\n    });\n  } catch (err) {\n    // Surface the timeout knob when the child was killed by our own timeout —\n    // otherwise the reporter sees a bare ETIMEDOUT / SIGTERM with no hint that\n    // `--timeout` even exists. Non-timeout errors flow through unchanged so the\n    // existing stderr-tail handling in `transcribeAudio` still applies.\n    throw wrapWhisperTimeoutError(err, {\n      effectiveTimeoutMs: whisperTimeoutMs,\n      model: effectiveModel,\n      wasOverride: options?.timeoutMs != null,\n    });\n  }\n\n  // 6. Read and validate output\n  const transcriptPath = `${outputBase}.json`;\n  if (!existsSync(transcriptPath)) {\n    throw new Error(\"Whisper did not produce output. Check the input file.\");\n  }\n\n  const transcript = JSON.parse(readFileSync(transcriptPath, \"utf-8\"));\n  const segments = transcript.transcription ?? [];\n\n  let wordCount = 0;\n  let maxEnd = 0;\n  for (const seg of segments) {\n    for (const token of seg.tokens ?? []) {\n      const text = (token.text ?? \"\").trim();\n      if (text && !text.startsWith(\"[_\") && !text.startsWith(\"[BLANK\")) wordCount++;\n      if (token.offsets?.to > maxEnd) maxEnd = token.offsets.to;\n    }\n  }\n\n  // 7. Detect speech onset before cleaning up the WAV\n  options?.onProgress?.(\"Detecting speech onset...\");\n  const speechOnsetSeconds = detectSpeechOnset(wavPath);","sourceCodeStart":488,"sourceCodeEnd":524,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/whisper/transcribe.ts#L488-L524","documentation":"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.","triggerScenarios":"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').","commonSituations":"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.","solutions":["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.","Give each transcribe() call a unique outputDir so concurrent runs cannot clobber each other's transcript.json (the base name 'transcript' is hard-coded).","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.","Verify the output directory is writable and has free disk space before the run."],"exampleFix":"// before\nawait transcribe(silentOrEmptyClip, sharedOutputDir);\n\n// after — guard duration and use a per-call output dir\nimport { statSync } from \"node:fs\";\nconst wav = prepareAudio(silentOrEmptyClip);\n// ffprobe / inspect: if duration is ~0, the source has no usable audio\nconst out = join(tmpdir(), `tx-${randomUUID()}`);\nawait transcribe(wav, out);","handlingStrategy":"try-catch","validationCode":"import { statSync, existsSync } from \"node:fs\";\n// pre-check the prepared WAV is non-empty and the output dir is writable & unique\nif (statSync(wavPath).size === 0) throw new Error('prepared WAV is empty — source has no audio');\nconst out = join(tmpdir(), `tx-${randomUUID()}`); // unique dir avoids collisions","typeGuard":"null","tryCatchPattern":"try {\n  await transcribe(input, uniqueOutDir);\n} catch (err) {\n  if (/did not produce output/.test(String(err))) {\n    // inspect the WAV with ffprobe; re-source silent/empty input; do not retry unchanged\n  } else throw err;\n}","preventionTips":["Give every concurrent transcribe() call a unique outputDir to avoid transcript.json collisions.","Verify the input has a non-zero audio stream (ffprobe) before transcribing.","Ensure the output directory is writable with free disk space.","Do not retry the identical command — re-source or repair the input first."],"tags":["whisper","transcription","runtime","audio","output"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}