{"record":{"id":"0505d3b836e859a2","repo":"harry0703/MoneyPrinterTurbo","slug":"elevenlabs-video-duration-is-invalid","errorCode":null,"errorMessage":"ElevenLabs video duration is invalid","messagePattern":"ElevenLabs video duration is invalid","errorType":"exception","errorClass":"ElevenLabsMusicError","httpStatus":null,"severity":"error","filePath":"app/services/elevenlabs_music.py","lineNumber":377,"sourceCode":"    finally:\n        _remove_file(temp_audio_path)\n\n\ndef generate_bgm(\n    video_path: str,\n    output_path: str,\n    video_duration: float,\n    prompt: str = \"\",\n) -> str:\n    \"\"\"为一条已拼接视频生成时长和画面匹配的 ElevenLabs 背景音乐。\"\"\"\n    if not get_api_key():\n        raise ElevenLabsMusicError(\"ElevenLabs API key is required\")\n    if not os.path.isfile(video_path):\n        raise ElevenLabsMusicError(\"ElevenLabs input video does not exist\")\n    try:\n        duration = float(video_duration)\n    except (TypeError, ValueError) as exc:\n        raise ElevenLabsMusicError(\n            \"ElevenLabs video duration is invalid\"\n        ) from exc\n    if not math.isfinite(duration) or duration <= 0:\n        raise ElevenLabsMusicError(\"ElevenLabs video duration is invalid\")\n    if duration > MAX_VIDEO_DURATION_SECONDS:\n        raise ElevenLabsMusicError(\n            \"ElevenLabs supports videos up to 600 seconds\"\n        )\n    prompt = str(prompt or \"\").strip()\n    if len(prompt) > MAX_PROMPT_LENGTH:\n        raise ElevenLabsMusicError(\n            \"ElevenLabs music prompt exceeds 1000 characters\"\n        )\n\n    proxy_path = \"\"\n    try:\n        proxy_path = _create_video_proxy(video_path)\n        return _request_bgm(proxy_path, output_path, prompt)","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/harry0703/MoneyPrinterTurbo/blob/1f9f19c2021a68d04df228f33e9099a0c947f6f8/app/services/elevenlabs_music.py#L359-L395","documentation":"First of two duration validation branches in generate_bgm: float(video_duration) raised TypeError (input was None or a non-numeric object) or ValueError (input was a string like '' or 'abc'). The value is coerced because task queues often deliver durations as strings.","triggerScenarios":"video_duration is None (upstream ffprobe failed and returned None), a string that is not parseable as a float, or an object whose __float__ raises.","commonSituations":"ffprobe failed silently and None propagated into the BGM task, JSON task payloads carrying an empty string for duration, or a refactor changing the duration type from float to a dataclass the code does not expect.","solutions":["Fix the upstream duration source: re-run ffprobe on the concatenated video and check why it returned a non-numeric value","Coerce to float before queueing the job: float(video_duration) at enqueue time surfaces the bug where it happens","Log the raw value and its type when this fires so the offending producer is identifiable"],"exampleFix":"# before\ngenerate_bgm(video, out, duration_from_queue, prompt)  # duration_from_queue = '' \n\n# after\nduration = float(ffprobe_duration(video))  # raises at the producer, not in the task\nif not duration > 0:\n    raise ValueError(f\"bad duration for {video}\")\ngenerate_bgm(video, out, duration, prompt)","handlingStrategy":"validation","validationCode":"duration = float(video_duration)  # coerce at enqueue; raises at the producer\nassert duration > 0","typeGuard":"def is_parseable_duration(v) -> bool:\n    try:\n        float(v)\n        return True\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":null,"preventionTips":["Coerce duration to float before queuing the task","Never pass None from a failed ffprobe — raise at the probe site"],"tags":["validation","type-coercion","guard"],"backgroundTag":null,"analyzedSha":"1f9f19c2021a68d04df228f33e9099a0c947f6f8","analyzedAt":"2026-08-14T19:41:05.568Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}