harry0703/MoneyPrinterTurbo · error · HttpException
{request_id}: requested range is not satisfiable
Error message
{request_id}: requested range is not satisfiable What it means
Raised by _parse_byte_range in app/controllers/v1/video.py (first branch) when the file being streamed has size <= 0. Even without a Range header the function refuses, because there is no satisfiable byte range for an empty (or zero-length) file and video playback would be undefined. Maps to HTTP 416.
Source
Thrown at app/controllers/v1/video.py:130
logger.warning(
f"skip unsafe task output path, request_id: {request_id}, path: {file}, "
f"error: {str(exc)}"
)
return file
relative_path = os.path.relpath(resolved_path, task_dir).replace("\\", "/")
uri_path = f"tasks/{relative_path}"
if endpoint:
return f"{endpoint.rstrip('/')}/{uri_path}"
return f"/{uri_path}"
def _parse_byte_range(
range_header: str | None, file_size: int, request_id: str
) -> tuple[int, int]:
"""解析单段 HTTP Range,并把无效或越界请求稳定转换成 416。"""
if file_size <= 0:
raise HttpException(
task_id=request_id,
status_code=416,
message=f"{request_id}: requested range is not satisfiable",
)
if not range_header:
return 0, file_size - 1
try:
# 视频播放器这里只需要单段 bytes range。拒绝多段请求可以避免返回体
# 与 Content-Range 不一致,也避免异常字符串落入 int() 产生 500。
if not range_header.startswith("bytes=") or "," in range_header:
raise ValueError("unsupported range format")
start_text, end_text = range_header[6:].split("-", 1)
if not start_text and not end_text:
raise ValueError("empty range")
if not start_text:View on GitHub (pinned to 1f9f19c202)
Solutions
- Verify the task actually finished successfully and re-check the artifact path returned by GET /tasks/{id}; regenerate if the output is empty.
- On the server, inspect the file with ls -l / os.path.getsize to confirm it is 0 bytes, then re-run generation.
- Guard clients: treat 416 on a stream as a corrupt-artifact signal, not a range negotiation problem.
Example fix
# before
resp = requests.get(stream_url, headers={"Range": "bytes=0-1023"})
assert resp.status_code == 206
# after
size = os.path.getsize(local_artifact) if local_artifact else None
if size is None or size <= 0:
task = requests.get(f"{base}/api/v1/tasks/{task_id}", headers=h).json()
stream_url = task["data"]["videos"][0] # fresh artifact
resp = requests.get(stream_url, headers={"Range": "bytes=0-1023"}) Defensive patterns
Strategy: validation
Validate before calling
size = os.path.getsize(video_path) # or Content-Length from a plain GET
if size <= 0:
raise ValueError("artifact is empty; regenerate the video") Type guard
def has_satisfiable_range(file_size: int) -> bool:
return isinstance(file_size, int) and file_size > 0 Try / catch
resp = requests.get(url, headers={"Range": "bytes=0-1023"})
if resp.status_code == 416:
# empty or corrupt artifact; re-fetch task and regenerate, don't re-negotiate ranges
task = get_task(task_id); url = task["videos"][0] Prevention
- Check Content-Length > 0 before a player attaches a Range request.
- Treat 416 on the first range request as a corrupt-artifact signal.
- Verify generation tasks report success before exposing stream URIs to players.
When it happens
Trigger: GET /api/v1/stream/<path> (with or without a Range header) where the resolved on-disk file is 0 bytes; a generation task produced an empty/partial output file; a file truncated externally after task completion.
Common situations: Upstream video generation silently wrote an empty file; disk-full during write; task marked complete before the artifact was flushed; deleting/truncating artifacts out from under a running server.
Related errors
- {request_id}: invalid file path
- background music file is empty
- ElevenLabs audio exceeds the 50 MB limit
- failed to request ElevenLabs music: {exc}
- Empty content in stream response
AI-assisted analysis of harry0703/MoneyPrinterTurbo@1f9f19c202 (2026-08-14).
Data as JSON: /api/errors/e05aa3bf8b7195f7.
Report an issue: GitHub.