harry0703/MoneyPrinterTurbo · error · HttpException

{request_id}: {str(exc)}

Error message

{request_id}: {str(exc)}

What it means

Raised by the POST BGM upload endpoint when bgm_service.save_bgm_upload raises BgmUploadError. This error class covers user-attributable problems: invalid or reserved filenames, unsupported extension, undecodable audio stream, non-seekable upload stream, or an empty file. The endpoint maps it to 400 with str(exc) embedded, logging request_id and reason at warning level without dumping file contents or absolute paths.

Source

Thrown at app/controllers/v1/video.py:353

        "Validate an MP3, M4A, AAC, WAV, FLAC, OGG, OPUS, or WMA file up to "
        "30 MB and store it under an immutable UUID filename in storage/bgm."
    ),
    responses={
        400: {"description": "The filename, format, size, or audio stream is invalid"},
        500: {"description": "FFmpeg validation or persistent storage is unavailable"},
    },
)
def upload_bgm_file(request: Request, file: UploadFile = File(...)):
    request_id = base.get_task_id(request)
    try:
        safe_filename = bgm_service.save_bgm_upload(file.filename, file.file)
    except bgm_service.BgmUploadError as exc:
        # 上传失败通常可以由用户更换文件后恢复,因此记录 request_id 和明确原因,
        # 但不输出文件内容或绝对路径,避免日志泄露用户数据。
        logger.warning(
            f"background music upload rejected: request_id={request_id}, error={str(exc)}"
        )
        raise HttpException(
            task_id=request_id,
            status_code=400,
            message=f"{request_id}: {str(exc)}",
        )
    except bgm_service.BgmServiceError as exc:
        # 工具链或存储故障属于服务端问题,不能伪装成用户文件错误。日志保留
        # request_id 和内部原因,HTTP 响应只返回稳定文案,避免暴露服务器路径。
        logger.error(
            f"background music upload failed: request_id={request_id}, error={str(exc)}"
        )
        raise HttpException(
            task_id=request_id,
            status_code=500,
            message=f"{request_id}: background music validation is unavailable",
        )

    response = {"file": safe_filename}
    return utils.get_response(200, response)

View on GitHub (pinned to 1f9f19c202)

Solutions

  1. Read the 400 body: str(exc) names the exact sub-cause (invalid filename / unsupported format / no decodable audio / etc.) and fix that specific condition.
  2. Re-export the audio to a mainstream format (MP3/M4A/WAV per SUPPORTED_BGM_EXTENSIONS) with ffmpeg before uploading.
  3. Use a plain basename filename free of Windows-reserved stems and control characters.
  4. For streams, ensure the file object is seekable (read the upload into BytesIO first).

Example fix

# before
files = {"file": ("CON.mp3", open("song.mp3", "rb"))}

# after
files = {"file": ("my-song.mp3", open("song.mp3", "rb"))}
Defensive patterns

Strategy: try-catch

Validate before calling

from pathlib import Path
suffix = Path(filename).suffix.lower()
if suffix not in SUPPORTED:  # mirror the server's supported extension set
    raise ValueError(f"convert to a supported format first: {sorted(SUPPORTED)}")

Type guard

import re
RESERVED = {"CON","PRN","AUX","NUL",*(f"COM{i}" for i in range(1,10)),*(f"LPT{i}" for i in range(1,10))}
def is_valid_bgm_filename(name: str) -> bool:
    n = (name or "").replace("\\", "/").split("/")[-1].strip()
    return (
        bool(n) and n not in {".", ".."} and len(n) <= 255
        and all(ord(c) >= 32 for c in n)
        and n.split(".", 1)[0].rstrip(" .").upper() not in RESERVED
        and Path(n).suffix.lower() in SUPPORTED
    )

Try / catch

try:
    resp = requests.post(bgm_url, files={"file": (name, fh)}, headers=h)
    resp.raise_for_status()
except requests.HTTPError as e:
    if e.response.status_code == 400:
        detail = e.response.json()["message"]  # embedded reason from BgmUploadError
        prompt_user_to_fix_file(detail)
    else:
        raise  # 500 = server-side BgmServiceError, not a user fix

Prevention

When it happens

Trigger: POST /api/v1/bgm (background music upload) with: filename CON.mp3 or NUL.wav (Windows reserved); extension .wma when unsupported; a text file renamed to .mp3 (FFmpeg decode fails); an empty upload; a SpooledTemporaryFile-backed stream lacking seek().

Common situations: Users uploading music bought in DRM'd or exotic formats; clients sending OS-generated names; a frontend uploading before the File object is fully materialized; names colliding with the server's internal upload prefix.

Related errors


AI-assisted analysis of harry0703/MoneyPrinterTurbo@1f9f19c202 (2026-08-14). Data as JSON: /api/errors/b614ed09c7119e1d. Report an issue: GitHub.