{"record":{"id":"66d9dca99452d293","repo":"harry0703/MoneyPrinterTurbo","slug":"minimax-tts-returned-audio-with-an-invalid-duratio","errorCode":null,"errorMessage":"MiniMax TTS returned audio with an invalid duration","messagePattern":"MiniMax TTS returned audio with an invalid duration","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"app/services/voice.py","lineNumber":1477,"sourceCode":"    output_dir = os.path.dirname(os.path.abspath(voice_file))\n    output_suffix = os.path.splitext(voice_file)[1] or \".mp3\"\n    temp_fd, temp_path = tempfile.mkstemp(\n        prefix=\".minimax-tts-\", suffix=output_suffix, dir=output_dir\n    )\n    os.close(temp_fd)\n\n    try:\n        with open(temp_path, \"wb\") as output:\n            output.write(audio_bytes)\n\n        audio_clip = AudioFileClip(temp_path)\n        try:\n            audio_duration = float(audio_clip.duration)\n        finally:\n            audio_clip.close()\n\n        if not math.isfinite(audio_duration) or audio_duration <= 0:\n            raise ValueError(\"MiniMax TTS returned audio with an invalid duration\")\n\n        os.replace(temp_path, voice_file)\n        return audio_duration\n    finally:\n        if os.path.exists(temp_path):\n            os.remove(temp_path)\n\n\ndef minimax_tts(text: str, voice_id: str, voice_rate: float, voice_file: str, voice_volume: float = 1.0) -> Union[SubMaker, None]:\n    \"\"\"Generate speech with the synchronous MiniMax T2A HTTP API.\"\"\"\n    text, voice_id = (text or \"\").strip(), (voice_id or \"\").strip()\n    if not text or not voice_id:\n        logger.error(\"MiniMax TTS requires text and a voice ID\")\n        return None\n    settings = config.minimax_tts\n    api_key = get_minimax_tts_api_key()\n    if not api_key:\n        logger.error(\"MiniMax TTS API key is not set\")","sourceCodeStart":1459,"sourceCodeEnd":1495,"githubUrl":"https://github.com/harry0703/MoneyPrinterTurbo/blob/1f9f19c2021a68d04df228f33e9099a0c947f6f8/app/services/voice.py#L1459-L1495","documentation":"After MiniMax TTS audio bytes are written to a temp file, the code opens it with moviepy AudioFileClip and reads duration. If duration is not finite or is <= 0 (empty/corrupt audio, zero-byte decode), it raises ValueError; the finally block removes the temp file so no partial output lands at voice_file.","triggerScenarios":"MiniMax returns a 200 response whose audio payload decodes to a zero-length or corrupt WAV/MP3; the container header is valid but frames are missing so moviepy reports duration 0 or NaN.","commonSituations":"Upstream serving a stub audio on overload; encoding change in MiniMax output that the local decoder mishandles; extremely short/whitespace-only input text producing empty audio.","solutions":["Retry the synthesis once — truncated audio from upstream is usually transient.","Check the input text is non-empty after stripping; avoid feeding only punctuation/whitespace.","If persistent, save and inspect the raw audio bytes returned by MiniMax (decode with ffprobe) to see whether corruption is upstream or local.","Update moviepy/ffmpeg — decoding of some MiniMax encodings depends on the local ffmpeg build."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"text = (text or \"\").strip()\nif not text:\n    raise ValueError(\"refusing to synthesize empty text\")","typeGuard":null,"tryCatchPattern":"try:\n    duration = minimax_tts_to_file(...)\nexcept ValueError as exc:\n    if \"invalid duration\" in str(exc):\n        return minimax_tts_to_file(...)  # one retry for truncated upstream audio\n    raise","preventionTips":["Never feed whitespace-only text to TTS.","Keep the local ffmpeg/moviepy stack current so MiniMax output decodes reliably."],"tags":["minimax","tts","audio","duration","moviepy"],"backgroundTag":null,"analyzedSha":"1f9f19c2021a68d04df228f33e9099a0c947f6f8","analyzedAt":"2026-08-14T19:41:05.568Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}