{"record":{"id":"6efb5a00070e8b35","repo":"heygen-com/hyperframes","slug":"synthesis-completed-but-no-output-file-was-created","errorCode":null,"errorMessage":"Synthesis completed but no output file was created","messagePattern":"Synthesis completed but no output file was created","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/tts/synthesize.ts","lineNumber":176,"sourceCode":"  // 4. Ensure output directory exists\n  mkdirSync(dirname(outputPath), { recursive: true });\n\n  // 5. Run synthesis\n  options?.onProgress?.(`Generating speech with voice ${voice} (${lang})...`);\n  try {\n    const espeakLang = ESPEAK_LANG_OVERRIDES[lang] ?? lang;\n    const stdout = execFileSync(\n      python,\n      [scriptPath, modelPath, voicesPath, text, voice, String(speed), outputPath, espeakLang],\n      {\n        encoding: \"utf-8\",\n        timeout: 300_000,\n        stdio: [\"pipe\", \"pipe\", \"pipe\"],\n      },\n    );\n\n    if (!existsSync(outputPath)) {\n      throw new Error(\"Synthesis completed but no output file was created\");\n    }\n\n    // Parse the last line of stdout as JSON (in case Python printed warnings before it)\n    const lines = stdout.trim().split(\"\\n\");\n    const jsonLine = lines[lines.length - 1] ?? \"\";\n    const result: {\n      outputPath: string;\n      sampleRate: number;\n      durationSeconds: number;\n      langApplied: boolean;\n    } = JSON.parse(jsonLine);\n\n    return {\n      outputPath: result.outputPath,\n      sampleRate: result.sampleRate,\n      durationSeconds: result.durationSeconds,\n      langApplied: result.langApplied,\n    };","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/tts/synthesize.ts#L158-L194","documentation":"The Python synth subprocess exited zero (execFileSync returned without throwing) AND the script's own stdout was produced, yet existsSync(outputPath) is false. The library treats this as a logic/environment failure: the script claimed success but the expected WAV is not at the agreed path. The output path is passed as argv[6] to the inline synth-v2.py script, which calls sf.write(output_path, samples, sample_rate).","triggerScenarios":"The Python script wrote to a different path than the TS side checks (path translation, symlink, or a script version mismatch where SCRIPT_PATH is stale); sf.write silently failed (disk full, permission denied) but the script did not raise; outputPath points at a directory that does not exist and sf.write raised but the error was swallowed; a custom model override wrote elsewhere.","commonSituations":"Stale cached synth script (~/.cache/hyperframes/tts/synth-v2.py from an older CLI) that used a different output convention; outputPath on a read-only or out-of-space filesystem; a race where a cleanup hook removed the file between sf.write and the existsSync check; Windows path quoting issues in argv passing a malformed output_path to Python.","solutions":["Delete the cached synth script so it regenerates: rm -rf ~/.cache/hyperframes/tts/synth-v2.py and rerun.","Verify the output directory exists and is writable: `mkdir -p $(dirname <outputPath>) && touch <outputPath>`.","Check free disk space and permissions on the output directory.","Inspect the cached synth-v2.py to confirm line 44 is `sf.write(output_path, samples, sample_rate)` — if not, the cache is stale.","Run the Python script manually with the same argv to see whether sf.write raises an exception the subprocess swallowed."],"exampleFix":"# before: stale cached script from an old CLI release\nrm ~/.cache/hyperframes/tts/synth-v2.py   # or: rm -rf ~/.cache/hyperframes/tts\n# then rerun; the current CLI rewrites synth-v2.py on next invoke","handlingStrategy":"try-catch","validationCode":"import { existsSync, mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\n// ensure the output path is writable BEFORE spending time on synthesis\nmkdirSync(dirname(outputPath), { recursive: true });\nconst probe = `${outputPath}.writeprobe`;\ntry {\n  require('node:fs').writeFileSync(probe, Buffer.alloc(0));\n  require('node:fs').unlinkSync(probe);\n} catch {\n  throw new Error(`Output path not writable: ${outputPath}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const r = await synthesize(text, out, { voice });\n  if (!existsSync(r.outputPath)) throw new Error('synthesis returned but file is missing');\n} catch (err) {\n  if (err instanceof Error && /completed but no output file/.test(err.message)) {\n    // clear the cached synth script and retry once\n    await fs.rm('~/.cache/hyperframes/tts/synth-v2.py');\n    return synthesize(text, out, { voice });\n  }\n  throw err;\n}","preventionTips":["Ensure the output directory exists and is writable before calling synthesize.","Periodically clear ~/.cache/hyperframes/tts to regenerate the synth script after CLI upgrades.","Check free disk space before long TTS jobs."],"tags":["tts","kokoro","output","filesystem","cache","subprocess"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}