harry0703/MoneyPrinterTurbo · error · BgmUploadError
background music upload must be binary
Error message
background music upload must be binary
What it means
Raised by stage_bgm_upload while streaming an upload to a temp file: a chunk returned by source.read() was not bytes, bytearray, or memoryview. This guards against callers passing a text-mode file object (io.StringIO, open(..., 'r')) or a mock/BytesIO-like object whose read() returns str. It fails mid-copy, so the staged temp file is removed and the original UploadedFile pointer is later restored in the finally block.
Source
Thrown at app/services/bgm.py:204
try:
source.seek(0)
except (AttributeError, OSError) as exc:
raise BgmUploadError("background music upload is not seekable") from exc
# 保留原始扩展名便于 FFmpeg 针对无容器头的 AAC 等格式选择正确的
# demuxer;临时文件仍放在目标目录,以保证最终 os.replace 是原子操作。
descriptor, temp_path = tempfile.mkstemp(
prefix=_INTERNAL_UPLOAD_PREFIX,
suffix=Path(safe_name).suffix.lower(),
dir=target_dir,
)
with os.fdopen(descriptor, "wb") as output:
while True:
chunk = source.read(_COPY_CHUNK_BYTES)
if not chunk:
break
if not isinstance(chunk, (bytes, bytearray, memoryview)):
raise BgmUploadError("background music upload must be binary")
total_bytes += len(chunk)
if total_bytes > MAX_BGM_UPLOAD_BYTES:
raise BgmUploadError("background music file exceeds the 30 MB limit")
output.write(chunk)
output.flush()
os.fsync(output.fileno())
if total_bytes == 0:
raise BgmUploadError("background music file is empty")
return safe_name, temp_path, total_bytes
except Exception as exc:
_remove_staged_file(temp_path)
if isinstance(exc, BgmUploadError):
raise
if isinstance(exc, OSError):
raise BgmServiceError("failed to stage background music upload") from exc
raise
finally:View on GitHub (pinned to 1f9f19c202)
Solutions
- Open/pass the upload source in binary mode: open(path, 'rb') or the original spooling file from the request, never a text wrapper.
- If the payload arrives as base64 or text, decode to bytes (base64.b64decode(...)) and wrap with io.BytesIO before calling the staging API.
- In tests, make the fake source return b'...' chunks (e.g. io.BytesIO(b'audio')) so read() yields bytes.
Example fix
// before source = open(upload_path, 'r') # text mode -> str chunks safe_name, temp_path, size = stage_bgm_upload(source, upload.filename) // after source = open(upload_path, 'rb') # binary mode -> bytes chunks safe_name, temp_path, size = stage_bgm_upload(source, upload.filename)
Defensive patterns
Strategy: type-guard
Validate before calling
def is_binary_source(source) -> bool:
probe = source.read(0)
source.seek(0) if hasattr(source, 'seek') else None
return isinstance(probe, (bytes, bytearray, memoryview)) Type guard
from typing import BinaryIO
def assert_binary_upload(source) -> None:
if not isinstance(source, (io.RawIOBase, io.BufferedIOBase)) and not hasattr(source, 'read'):
raise TypeError('upload source must be a binary file object')
sample = source.read(1)
if isinstance(sample, str):
raise TypeError('upload source is text-mode; reopen in \'rb\'')
if hasattr(source, 'seek'):
source.seek(0) Try / catch
try:
stage_bgm_upload(source, filename)
except BgmUploadError as e:
if 'must be binary' in str(e):
source = io.BytesIO(original_bytes) # reopen as binary and retry once Prevention
- Always open uploads with mode='rb' and pass request bodies straight through without TextIO wrappers.
- Decode base64/text payloads to bytes and wrap in io.BytesIO before staging.
- Make test doubles return bytes chunks so type assumptions match production.
When it happens
Trigger: Calling the BGM upload staging function with a file opened in text mode ('rb' omitted), an io.StringIO, or a custom BinaryIO stub whose read() returns str. Any non-bytes chunk on the very first or a later 1 MB read triggers it.
Common situations: FastAPI/Streamlit handlers re-wrapping the upload in a TextIOWrapper; test fixtures mocking the upload source with str chunks; passing an already-decoded payload (base64 str) instead of raw bytes.
Related errors
- {request_id}: {str(exc)}
- invalid background music filename
- unsupported background music format; supported formats: {sup
- uploaded file must contain a decodable audio stream
- background music upload is not seekable
AI-assisted analysis of harry0703/MoneyPrinterTurbo@1f9f19c202 (2026-08-14).
Data as JSON: /api/errors/2392b12130961689.
Report an issue: GitHub.