harry0703/MoneyPrinterTurbo · error · SoniloError
Sonilo returned an invalid streaming event
Error message
Sonilo returned an invalid streaming event
What it means
Raised by _parse_event when a line parses as valid JSON but is not a JSON object, or lacks a string 'type' field. Every Sonilo streaming event is required to carry a string discriminator (error, title, complete, audio_chunk); a top-level array, a bare string or number, or an event with a missing or non-string type is treated as a protocol violation.
Source
Thrown at app/services/sonilo.py:210
raise SoniloError(f"failed to generate Sonilo video proxy: {detail}")
proxy_size = os.path.getsize(proxy_path) if os.path.isfile(proxy_path) else 0
if proxy_size <= 0 or proxy_size > MAX_PROXY_BYTES:
_remove_file(proxy_path)
raise SoniloError("Sonilo video proxy is empty or exceeds the 300 MB limit")
logger.info(
f"Sonilo video proxy prepared: source={video_path}, size={proxy_size} bytes"
)
return proxy_path
def _parse_event(raw_line: bytes) -> dict[str, Any]:
"""严格解析单条 NDJSON,禁止静默忽略截断或非对象响应。"""
try:
event = json.loads(raw_line.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SoniloError("Sonilo returned malformed streaming data") from exc
if not isinstance(event, dict) or not isinstance(event.get("type"), str):
raise SoniloError("Sonilo returned an invalid streaming event")
return event
def _stream_audio(response: requests.Response, temp_audio_path: str) -> tuple[int, str]:
"""
把第一条配乐流按事件顺序写入临时文件,并限制最大体积。
API 可能同时返回多条候选流;当前产品只需要一条 BGM,所以固定选择
stream_index=0。只有收到 complete 事件并通过 FFmpeg 完整解码后才会发布。
"""
total_bytes = 0
title = ""
completed = False
with open(temp_audio_path, "wb") as output:
for raw_line in response.iter_lines():
if not raw_line:
continue
event = _parse_event(raw_line)View on GitHub (pinned to 1f9f19c202)
Solutions
- Log the offending parsed value (temporarily log the event before the isinstance checks) to see its actual shape.
- Check the Sonilo changelog and docs for a streaming schema change (for example type renamed) and update the checks in _parse_event accordingly.
- Fix test fixtures to include a string type field on every streamed line.
- If a keep-alive or comment line is the cause, skip it explicitly by exact match instead of loosening the object check.
Defensive patterns
Strategy: type-guard
Type guard
def is_valid_sonilo_event(event: object) -> bool:
return isinstance(event, dict) and isinstance(event.get("type"), str) Try / catch
try:
event = _parse_event(raw_line)
except SoniloError as exc:
if "invalid streaming event" in str(exc):
logger.warning(f"protocol drift, raw line: {raw_line[:200]!r}")
raise # strict policy: never skip unknown shapes silently
raise Prevention
- Keep streaming fixtures in tests that cover every documented event type plus a malformed line.
- Subscribe to the provider changelog and update _parse_event the same day the stream schema changes.
When it happens
Trigger: The provider sends bare payloads such as the string ok or a JSON array on the stream; an event object where type is null or numeric; a partial JSON document that happens to parse to a non-dict; an API version change renaming type to event or kind.
Common situations: Provider protocol evolution; test harnesses streaming fixtures without the type field; middleware injecting keep-alive JSON scalars between events.
Related errors
- Sonilo returned an empty audio chunk
- Sonilo returned an invalid service list
- Sonilo returned malformed streaming data
- Sonilo generation failed: {message}
- Sonilo returned an invalid audio chunk
AI-assisted analysis of harry0703/MoneyPrinterTurbo@1f9f19c202 (2026-08-14).
Data as JSON: /api/errors/edad1fe92d09c255.
Report an issue: GitHub.