{"record":{"id":"48ae7de08eeb51ca","repo":"jeecgboot/JeecgBoot","slug":"edge-tts-exitcode","errorCode":null,"errorMessage":"edge-tts 执行失败，退出码: {exitCode}","messagePattern":"edge-tts 执行失败，退出码: (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/video/service/impl/VideoGenerationServiceImpl.java","lineNumber":553,"sourceCode":"    }\n\n    /**\n     * 使用 edge-tts 将文本转为语音\n     */\n    private void generateTtsAudio(String text, Path audioPath) throws IOException, InterruptedException {\n        ProcessBuilder pb = new ProcessBuilder(\n                edgeTtsPath,\n                \"--voice\", \"zh-CN-YunyangNeural\",\n                \"--text\", text,\n                \"--write-media\", audioPath.toAbsolutePath().toString()\n        );\n        pb.redirectErrorStream(true);\n        Process process = pb.start();\n        String output = new String(process.getInputStream().readAllBytes());\n        int exitCode = process.waitFor();\n        if (exitCode != 0) {\n            log.error(\"edge-tts 执行失败: {}\", output);\n            throw new RuntimeException(\"edge-tts 执行失败，退出码: \" + exitCode);\n        }\n    }\n\n    /**\n     * 视频生成成功后，自动生成语音并合并（参照 addVoiceover 逻辑）\n     * 使用 TTS API（VoiceApiHelper）生成语音，ffmpeg 合并\n     * 失败时降级返回无声视频，不影响主流程\n     *\n     * @param taskId         任务ID\n     * @param prompt         用户原始prompt\n     * @param localVideoPath 本地无声视频相对路径（如 video/video_xxx.mp4）\n     * @param result         结果对象，成功时更新 videoUrl 和 narration\n     */\n    private void autoAddVoiceover(String taskId, String prompt, String localVideoPath, VideoTaskResultVo result) {\n        log.info(\">>> autoAddVoiceover 开始: taskId={}, ffmpeg={}, localVideoPath={}\", taskId, ffmpegPath, localVideoPath);\n        if (ffmpegPath == null) {\n            log.info(\"ffmpeg不可用，跳过自动配音: taskId={}\", taskId);\n            return;","sourceCodeStart":535,"sourceCodeEnd":571,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/video/service/impl/VideoGenerationServiceImpl.java#L535-L571","documentation":"Thrown by VideoGenerationServiceImpl.generateTtsAudio() after spawning the edge-tts command-line tool as a subprocess. edge-tts is a Python CLI that synthesizes speech through Microsoft Edge's online TTS endpoint. The error fires when the process exits with a non-zero code -- the binary is missing, the server has no outbound internet, or the argument list is malformed. The captured stdout/stderr (redirectErrorStream=true) is logged but NOT included in the thrown message.","triggerScenarios":"generateTtsAudio() runs edgeTtsPath with a fixed voice 'zh-CN-YunyangNeural' and the supplied text. A non-zero exit occurs when: edgeTtsPath is unset/wrong; edge-tts cannot reach speech.platform.bing.com; the text is empty or exceeds edge-tts limits; the Python environment/edge-tts version is broken.","commonSituations":"Production server without edge-tts installed; edgeTtsPath pointing to a venv that no longer exists; egress firewall blocking the Microsoft TTS host; very long narration text; edge-tts package updated and CLI flags changed.","solutions":["Find the captured process output in the log line 'edge-tts 执行失败: {}' -- it holds the real stderr.","Run the binary at edgeTtsPath manually: `edge-tts --voice zh-CN-YunyangNeural --text test --write-media /tmp/t.mp3` to reproduce.","Confirm outbound HTTPS to speech.platform.bing.com / the edge-tts endpoint works from the server.","Install or upgrade edge-tts (`pip install -U edge-tts`) and align edgeTtsPath to the working binary.","Guard empty/oversized text before calling generateTtsAudio."],"exampleFix":"// before\nint exitCode = process.waitFor();\nif (exitCode != 0) {\n    log.error(\"edge-tts 执行失败: {}\", output);\n    throw new RuntimeException(\"edge-tts 执行失败，退出码: \" + exitCode);\n}\n// after - surface the captured output and validate inputs\nif (text == null || text.isBlank()) {\n    throw new IllegalArgumentException(\"edge-tts 文本为空\");\n}\nint exitCode = process.waitFor();\nif (exitCode != 0) {\n    throw new RuntimeException(\"edge-tts 执行失败，退出码: \" + exitCode + \"，输出: \" + output);\n}","handlingStrategy":"validation","validationCode":"// run before calling generateTtsAudio\nprivate void ensureEdgeTtsReady(String edgeTtsPath, String text) throws IOException {\n    if (text == null || text.isBlank()) {\n        throw new IllegalArgumentException(\"edge-tts 文本为空\");\n    }\n    Path bin = Paths.get(edgeTtsPath);\n    if (!Files.isExecutable(bin)) {\n        throw new IllegalStateException(\"edge-tts 不可执行: \" + edgeTtsPath);\n    }\n}","typeGuard":null,"tryCatchPattern":"// caller already degrades gracefully -- keep this pattern\ntry {\n    generateTtsAudio(text, audioPath);\n} catch (Exception e) {\n    log.error(\">>> edge-tts 失败，降级返回无声视频: taskId={}\", taskId, e);\n    // do NOT rethrow -- fall back to silent video\n}","preventionTips":["Verify edgeTtsPath on startup and log a clear warning if it is not executable.","Smoke-test edge-tts in CI/staging with a fixed sample text.","Cap text length before calling to stay within edge-tts limits."],"tags":["tts","edge-tts","subprocess","video","external-process","network"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}