{"record":{"id":"b12c384230b7ecdf","repo":"heygen-com/hyperframes","slug":"model-download-failed-model-b12c38","errorCode":null,"errorMessage":"Model download failed: ${model}","messagePattern":"Model download failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/whisper/manager.ts","lineNumber":226,"sourceCode":"\n  // 4. Give up — tell the user how\n  throw new WhisperUnavailableError(`whisper-cpp not found. Install: ${getInstallInstructions()}`);\n}\n\nexport async function ensureModel(\n  model: string = DEFAULT_MODEL,\n  options?: { onProgress?: (message: string) => void },\n): Promise<string> {\n  const modelPath = join(MODELS_DIR, `ggml-${model}.bin`);\n  if (existsSync(modelPath)) return modelPath;\n\n  mkdirSync(MODELS_DIR, { recursive: true });\n\n  options?.onProgress?.(`Downloading model ${model}...`);\n  await downloadFile(getModelUrl(model), modelPath);\n\n  if (!existsSync(modelPath)) {\n    throw new Error(`Model download failed: ${model}`);\n  }\n\n  return modelPath;\n}\n\nexport function hasFFmpeg(): boolean {\n  return findFFmpeg() !== undefined;\n}\n\nexport { MODELS_DIR, DEFAULT_MODEL };\n","sourceCodeStart":208,"sourceCodeEnd":237,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/whisper/manager.ts#L208-L237","documentation":"Thrown by ensureModel when the whisper model file download completed (downloadFile resolved without throwing) but the model file does not exist at the expected path afterward. This catches the edge case where downloadFile appears to succeed but the file is missing — e.g. a redirect to an error page that wrote nothing, or the file was written to a different location.","triggerScenarios":"downloadFile resolves successfully (HTTP 200, writeStream finished) but existsSync returns false for the model path; the download was interrupted in a way that closed the stream without throwing; a disk error prevented the file from being written; the HuggingFace URL returned an empty or truncated response that was written then removed.","commonSituations":"Network instability causing a partial download that was cleaned up; disk full; permissions issue on the models cache directory (~/.cache/hyperframes/whisper/models); HuggingFace CDN serving a truncated file.","solutions":["Check disk space in the models cache directory (~/.cache/hyperframes/whisper/models).","Retry the command — the next attempt will re-download since the file doesn't exist.","Manually download the model from the HuggingFace URL and place it at the expected path.","Verify network connectivity and that huggingface.co is reachable."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nfunction isModelCached(model: string): boolean {\n  return existsSync(join(homedir(), \".cache\", \"hyperframes\", \"whisper\", \"models\", `ggml-${model}.bin`));\n}","typeGuard":null,"tryCatchPattern":"async function ensureModelWithRetry(model: string, maxRetries = 2): Promise<string> {\n  for (let i = 0; i <= maxRetries; i++) {\n    try {\n      return await ensureModel(model);\n    } catch (err) {\n      if (err instanceof Error && err.message.includes(\"Model download failed\") && i < maxRetries) {\n        await new Promise((r) => setTimeout(r, 2000));\n        continue;\n      }\n      throw err;\n    }\n  }\n  throw new Error(\"Model download failed after retries\");\n}","preventionTips":["Pre-download the model before running transcription in CI to avoid network-dependent failures.","Ensure the models cache directory (~/.cache/hyperframes/whisper/models) is writable.","Check disk space before downloading models (~500MB for small.en)."],"tags":["whisper","download","model","network"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}