{"record":{"id":"70c7e1df235f889f","repo":"harry0703/MoneyPrinterTurbo","slug":"sonilo-returned-an-empty-audio-chunk","errorCode":null,"errorMessage":"Sonilo returned an empty audio chunk","messagePattern":"Sonilo returned an empty audio chunk","errorType":"exception","errorClass":"SoniloError","httpStatus":null,"severity":"error","filePath":"app/services/sonilo.py","lineNumber":250,"sourceCode":"                    event.get(\"message\") or event.get(\"error\") or \"unknown error\"\n                )\n                raise SoniloError(f\"Sonilo generation failed: {message}\")\n            if event_type == \"title\":\n                title = str(event.get(\"title\") or event.get(\"data\") or \"\")[:200]\n                continue\n            if event_type == \"complete\":\n                completed = True\n                break\n            if event_type != \"audio_chunk\":\n                logger.debug(f\"ignoring unsupported Sonilo event: type={event_type}\")\n                continue\n\n            stream_index = event.get(\"stream_index\", 0)\n            if stream_index != 0:\n                continue\n            encoded_chunk = event.get(\"data\") or event.get(\"audio\")\n            if not isinstance(encoded_chunk, str) or not encoded_chunk:\n                raise SoniloError(\"Sonilo returned an empty audio chunk\")\n            try:\n                chunk = base64.b64decode(encoded_chunk, validate=True)\n            except (binascii.Error, ValueError) as exc:\n                raise SoniloError(\"Sonilo returned an invalid audio chunk\") from exc\n            if not chunk:\n                raise SoniloError(\"Sonilo returned an empty audio chunk\")\n            total_bytes += len(chunk)\n            if total_bytes > MAX_GENERATED_AUDIO_BYTES:\n                raise SoniloError(\"Sonilo audio exceeds the 30 MB limit\")\n            output.write(chunk)\n        output.flush()\n        os.fsync(output.fileno())\n\n    if not completed:\n        raise SoniloError(\"Sonilo stream ended before completion\")\n    if total_bytes <= 0:\n        raise SoniloError(\"Sonilo returned no audio data\")\n    return total_bytes, title","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/harry0703/MoneyPrinterTurbo/blob/1f9f19c2021a68d04df228f33e9099a0c947f6f8/app/services/sonilo.py#L232-L268","documentation":"Raised on an audio_chunk event for stream_index 0 whose payload (the 'data' or 'audio' field) is missing, None, or not a non-empty string. Base64-encoded audio is expected; anything else (an empty string, a nested object, differently encoded bytes) aborts the stream write immediately.","triggerScenarios":"A stream event of type audio_chunk with stream_index 0 but no data field; data explicitly null; the provider sends raw bytes encoded differently (for example hex) or an empty-string placeholder as keep-alive; a schema change moving audio to a new field name.","commonSituations":"Provider API revision renaming the audio field; the server sending heartbeat chunk events with no payload; an upstream bug emitting chunks before audio is ready.","solutions":["Temporarily log the event keys for failing chunks to see which field carries audio now (data vs audio vs a new name).","If heartbeats: skip zero-payload audio_chunk events instead of failing, but only after confirming with provider docs that they are legal.","If the field was renamed, update the data-or-audio lookup in app/services/sonilo.py:249.","Report persistent empty chunks to Sonilo support with a timestamp and request id."],"exampleFix":"# before\nencoded_chunk = event.get(\"data\") or event.get(\"audio\")\n# after (tolerate provider heartbeat chunks after confirming legality)\nencoded_chunk = event.get(\"data\") or event.get(\"audio\")\nif encoded_chunk is None and event.get(\"heartbeat\"):\n    continue","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def has_audio_payload(event: dict) -> bool:\n    payload = event.get(\"data\") or event.get(\"audio\")\n    return isinstance(payload, str) and len(payload) > 0","tryCatchPattern":"try:\n    audio = request_bgm(video, out, prompt)\nexcept SoniloError as exc:\n    if \"empty audio chunk\" in str(exc):\n        log_stream_context_and_report_to_provider()  # protocol violation upstream\n    raise","preventionTips":["Track provider API versions; the data/audio field fallback exists because of past renames, so extend it promptly.","Capture the failing event's keys in debug logs to diagnose schema drift in one attempt."],"tags":["sonilo","streaming","audio","protocol"],"backgroundTag":null,"analyzedSha":"1f9f19c2021a68d04df228f33e9099a0c947f6f8","analyzedAt":"2026-08-14T19:41:05.568Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}