harry0703/MoneyPrinterTurbo · error · BgmUploadError
uploaded file must contain a decodable audio stream
Error message
uploaded file must contain a decodable audio stream
What it means
Raised by _validate_audio in app/services/bgm.py when the FFmpeg full-decode probe exits non-zero. The probe runs FFmpeg with -map 0:a:0 (fails when there is no audio stream) and -xerror (promotes decode errors to failure), decoding to null output; full decoding also catches encrypted or random data that merely carries an audio extension. A file that passes the extension check but fails this probe is rejected as not containing a decodable audio stream (BgmUploadError → HTTP 400).
Source
Thrown at app/services/bgm.py:154
"-xerror",
"-i",
file_path,
"-map",
"0:a:0",
"-f",
"null",
"-",
],
capture_output=True,
timeout=timeout_seconds,
check=False,
)
except subprocess.TimeoutExpired as exc:
raise BgmServiceError("FFmpeg background music validation timed out") from exc
except OSError as exc:
raise BgmServiceError("failed to run FFmpeg for background music validation") from exc
if decoded.returncode != 0:
raise BgmUploadError("uploaded file must contain a decodable audio stream")
def validate_audio_file(file_path: str, timeout_seconds: int = 120) -> None:
"""
校验磁盘上的音频文件可由项目 FFmpeg 完整解码。
上传预检通常只需 30 秒;Sonilo 生成的配乐最长可达 6 分钟,因此对外提供
可调整超时的复用入口。服务只依赖 FFmpeg,不要求系统额外安装 FFprobe。
"""
if not os.path.isfile(file_path) or os.path.getsize(file_path) <= 0:
raise BgmUploadError("background music file is empty or missing")
_validate_audio(file_path, timeout_seconds=timeout_seconds)
def _stage_bgm_upload(filename: str, source: BinaryIO) -> tuple[str, str, int]:
"""
将上传流写入同目录临时文件,并返回安全文件名、临时路径和字节数。
View on GitHub (pinned to 1f9f19c202)
Solutions
- Verify locally first with the same test: ffmpeg -v error -i file -map 0:a:0 -xerror -f null - ; a non-zero exit means re-encode or re-download the file.
- Re-encode with ffmpeg -i input -vn -c:a libmp3lame output.mp3 to strip anything non-audio and rebuild a clean stream.
- If the file is DRM-protected, obtain a DRM-free copy; no server setting will accept it.
Example fix
# before: upload raw, hope for the best
files = {"file": ("maybe-broken.mp3", open("maybe-broken.mp3", "rb"))}
# after: validate locally with the server's own probe, re-encode if needed
import subprocess, sys
rc = subprocess.run(["ffmpeg", "-v", "error", "-i", "maybe-broken.mp3", "-map", "0:a:0", "-xerror", "-f", "null", "-"]).returncode
if rc != 0:
subprocess.run(["ffmpeg", "-y", "-i", "maybe-broken.mp3", "-vn", "-c:a", "libmp3lane" if False else "libmp3lame", "fixed.mp3"], check=True)
files = {"file": ("fixed.mp3", open("fixed.mp3", "rb"))} Defensive patterns
Strategy: validation
Validate before calling
import subprocess
def is_decodable_audio(path: str) -> bool:
return subprocess.run(
["ffmpeg", "-v", "error", "-i", path, "-map", "0:a:0", "-xerror", "-f", "null", "-"],
capture_output=True,
).returncode == 0 Try / catch
try:
validate_upload(name, fh)
except BgmUploadError as e:
if "decodable audio" in str(e):
reencode_to_mp3(local_path) # ffmpeg -i in -vn -c:a libmp3lame out.mp3
retry_with(reencoded_file)
else:
raise Prevention
- Run the same ffmpeg -xerror probe locally before uploading.
- Re-encode uploads once at ingest instead of retrying raw corrupt files.
- Treat extension checks as cheap pre-filters, not proof of audio content.
When it happens
Trigger: Uploading an image/document renamed to .mp3; DRM-protected audio; a truncated/corrupt audio file where the container header exists but frames are broken; an MP4 with video only (no audio stream).
Common situations: Corrupted downloads (interrupted transfer); music services exporting DRM'd files; users renaming files to force acceptance; files with cover-art-only streams and no audio track.
Related errors
- background music file exceeds the 30 MB limit
- background music file is empty
- {request_id}: {str(exc)}
- {request_id}: background music validation is unavailable
- invalid background music filename
AI-assisted analysis of harry0703/MoneyPrinterTurbo@1f9f19c202 (2026-08-14).
Data as JSON: /api/errors/d6620a4c5c1eca47.
Report an issue: GitHub.