harry0703/MoneyPrinterTurbo · error · SoniloError
Sonilo returned an invalid service response
Error message
Sonilo returned an invalid service response
What it means
Raised by sonilo.test_connection() when response.json() throws ValueError on the 2xx services response — the body is not valid JSON (HTML error page, empty body, gzip/proxy corruption). The original parse error is chained with `from exc`.
Source
Thrown at app/services/sonilo.py:107
if not api_key:
raise SoniloError("Sonilo API key is required")
try:
response = requests.get(
f"{_base_url()}{SERVICES_PATH}",
headers={"Authorization": f"Bearer {api_key}"},
timeout=(15, 30),
)
except requests.RequestException as exc:
raise SoniloError(f"failed to connect to Sonilo: {exc}") from exc
if not response.ok:
raise SoniloError(
f"Sonilo connection failed ({response.status_code}): "
f"{_safe_response_error(response)}"
)
try:
payload = response.json()
except ValueError as exc:
raise SoniloError("Sonilo returned an invalid service response") from exc
if not isinstance(payload, dict):
raise SoniloError("Sonilo returned an unexpected service response")
available_services = payload.get("available_services")
if not isinstance(available_services, list) or not all(
isinstance(service_id, str) for service_id in available_services
):
raise SoniloError("Sonilo returned an invalid service list")
normalized_services = {
_normalize_service_id(service_id) for service_id in available_services
}
if VIDEO_TO_MUSIC_SERVICE_ID not in normalized_services:
raise SoniloError("Sonilo video-to-music service is not available for this key")
logger.info("Sonilo connection test succeeded")
return payload
def _remove_file(file_path: str) -> None:
"""尽力清理 Sonilo 中间文件,不覆盖调用方正在处理的原始异常。"""View on GitHub (pinned to 1f9f19c202)
Solutions
- Check sonilo_base_url — pointing at a web page host instead of the API host is the most common cause
- Inspect the raw body: log response.headers.get('content-type') and response.text[:200] in a debug run to see HTML vs JSON
- Bypass the captive/transparent proxy or fix proxy SSL/encoding handling
- If content-type is consistently non-JSON from the API itself, the endpoint/path is wrong — use the default base_url
Example fix
# debug snippet to identify the offending body
resp = requests.get(f"{_base_url()}{SERVICES_PATH}", headers=headers, timeout=(15, 30))
print(resp.status_code, resp.headers.get("content-type"), resp.text[:200]) Defensive patterns
Strategy: validation
Validate before calling
# pre-check that the endpoint returns JSON before parsing
resp = requests.get(url, headers=headers, timeout=(15, 30))
if 'application/json' not in (resp.headers.get('content-type') or ''):
raise SoniloError(f"non-JSON content-type: {resp.headers.get('content-type')}") Try / catch
except SoniloError as e: if 'invalid service response' in str(e): non-retryable in place — inspect content-type/body; if a proxy caused it, fix the proxy then retry
Prevention
- Always check response content-type before .json() at third-party boundaries
- Avoid custom base_url values pointing at non-API hosts
- Log the first 200 bytes of unexpected bodies for triage
When it happens
Trigger: requests.get to /v1/account/services returns 200 but body is HTML (captive portal, CDN error page), truncated (proxy buffering issue), or BOM-prefixed/plain text; response.json() then raises requests.exceptions.JSONDecodeError (a ValueError subclass).
Common situations: Captive portals / hotel WiFi injecting HTML on 200; transparent proxies mangling gzip encoding; Sonilo CDN edge returning an HTML error with status 200; sonilo_base_url pointing at a web UI host rather than the API host.
Related errors
- ElevenLabs returned an invalid subscription response
- Sonilo returned an unexpected service response
- Sonilo returned an invalid service list
- Sonilo returned malformed streaming data
- MiniMax get_voice returned invalid JSON
AI-assisted analysis of harry0703/MoneyPrinterTurbo@1f9f19c202 (2026-08-14).
Data as JSON: /api/errors/c40e8fa6a2c6af70.
Report an issue: GitHub.