harry0703/MoneyPrinterTurbo · error · SoniloError
Sonilo stream ended before completion
Error message
Sonilo stream ended before completion
What it means
Raised when the streaming response ends (iter_lines exhausted, connection closed) without a complete event ever arriving. Per the protocol, audio chunks are only publishable after the provider signals completion; an unterminated stream is treated as a truncated generation and the temp file is never promoted to the output path.
Source
Thrown at app/services/sonilo.py:265
continue
encoded_chunk = event.get("data") or event.get("audio")
if not isinstance(encoded_chunk, str) or not encoded_chunk:
raise SoniloError("Sonilo returned an empty audio chunk")
try:
chunk = base64.b64decode(encoded_chunk, validate=True)
except (binascii.Error, ValueError) as exc:
raise SoniloError("Sonilo returned an invalid audio chunk") from exc
if not chunk:
raise SoniloError("Sonilo returned an empty audio chunk")
total_bytes += len(chunk)
if total_bytes > MAX_GENERATED_AUDIO_BYTES:
raise SoniloError("Sonilo audio exceeds the 30 MB limit")
output.write(chunk)
output.flush()
os.fsync(output.fileno())
if not completed:
raise SoniloError("Sonilo stream ended before completion")
if total_bytes <= 0:
raise SoniloError("Sonilo returned no audio data")
return total_bytes, title
def _request_bgm(video_path: str, output_path: str, prompt: str) -> str:
"""请求配乐并在完整协议及音频校验通过后原子保存。"""
output_dir = os.path.dirname(os.path.abspath(output_path))
os.makedirs(output_dir, exist_ok=True)
descriptor, temp_audio_path = tempfile.mkstemp(
prefix=".sonilo-audio-",
suffix=Path(output_path).suffix or ".m4a",
dir=output_dir,
)
os.close(descriptor)
try:
logger.info(
f"requesting Sonilo background music: video={video_path}, "View on GitHub (pinned to 1f9f19c202)
Solutions
- If a reverse proxy fronts the app or the Sonilo base URL, raise its streaming and idle timeouts above the worst generation time.
- Check the sonilo_timeout config: the read timeout caps how long the stream may stall; ensure it exceeds the real generation length (capped at 1800 seconds in _request_timeout).
- Retry the request; an unterminated stream is usually transient.
- If persistent, capture the tail of the stream (last event types) and check the provider status and changelog for terminal-event renames.
Example fix
# nginx before
location / { proxy_read_timeout 60s; }
# after
location / { proxy_read_timeout 1200s; proxy_buffering off; } Defensive patterns
Strategy: retry
Try / catch
try:
audio = request_bgm(video, out, prompt)
except SoniloError as exc:
if "stream ended before completion" in str(exc):
# truncated stream: safe to retry the whole request;
# the library already discards the partial temp file
retry_with_backoff(request_bgm, attempts=2, base_delay=5)
raise Prevention
- Set all intermediary read and idle timeouts (nginx proxy_read_timeout, load-balancer idle timeout) above worst-case generation time.
- Keep the sonilo_timeout config near the 1800-second ceiling for long generations; short timeouts guarantee this error.
When it happens
Trigger: The server closes the connection mid-generation (idle timeout, crash, load-balancer cut); the network drops between the last audio_chunk and the terminal complete event; the provider sends a different terminal event name (for example done) after an API change; generation is aborted server-side without an error event.
Common situations: Long generations exceeding an intermediary's idle or stream timeout (nginx proxy_read_timeout, ALB defaults); flaky networks; provider rolling restarts during generation.
Related errors
- failed to connect to Sonilo: {exc}
- Sonilo returned malformed streaming data
- failed to request Sonilo music: {exc}
- edge_tts stream timed out after {timeout_seconds:g}s
- failed to connect to ElevenLabs: {exc}
AI-assisted analysis of harry0703/MoneyPrinterTurbo@1f9f19c202 (2026-08-14).
Data as JSON: /api/errors/3c06c3a9d4eef029.
Report an issue: GitHub.