babysor/MockingBird · warning · RuntimeError
/emotion-enhance returned no data: {resp.text}
Error message
/emotion-enhance returned no data: {resp.text} What it means
Raised when /emotion-enhance answers 200 but data.emotion_enhance is missing or falsy in the JSON — a response-shape mismatch during timeline rendering.
Source
Thrown at skills/speak/scripts/render_timeline.py:216
def _noiz_emotion_enhance(
base_url: str, api_key: str, text: str, timeout: int
) -> str:
import requests # noqa: delayed import so kokoro path doesn't need requests
resp = requests.post(
f"{base_url.rstrip('/')}/emotion-enhance",
headers={"Authorization": api_key, "Content-Type": "application/json"},
json={"text": text},
timeout=timeout,
)
if resp.status_code != 200:
raise RuntimeError(
f"/emotion-enhance failed: status={resp.status_code}, body={resp.text}"
)
enhanced = resp.json().get("data", {}).get("emotion_enhance")
if not enhanced:
raise RuntimeError(f"/emotion-enhance returned no data: {resp.text}")
return enhanced
def _bool_form(v: Any) -> str:
return "true" if bool(v) else "false"
def _resolve_reference_audio(ref: str, timeout: int) -> Tuple[Path, Optional[Path]]:
"""Resolve reference_audio to a path. If ref is a URL, download to temp file.
Returns (path_to_use, temp_path_to_cleanup_or_None)."""
if ref.startswith("http://") or ref.startswith("https://"):
import requests
tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
tmp.close()
r = requests.get(ref, timeout=timeout)
r.raise_for_status()
Path(tmp.name).write_bytes(r.content)
return Path(tmp.name), Path(tmp.name)View on GitHub (pinned to 28dc5e14f1)
Solutions
- Log resp.text to inspect the actual payload
- Skip enhancement for that cue and use the raw cue text
- Confirm the API schema version matches data.emotion_enhance
- Strip/skip empty-text cues before rendering
Example fix
# before
enhanced = resp.json().get("data", {}).get("emotion_enhance")
if not enhanced:
raise RuntimeError(...)
# after
enhanced = resp.json().get("data", {}).get("emotion_enhance") or cue.text Defensive patterns
Strategy: fallback
Validate before calling
if not cue.text.strip():
continue # skip empty cues before enhancement Try / catch
try:
enhanced = _noiz_emotion_enhance(...)
except RuntimeError as e:
if 'no data' in str(e): enhanced = cue.text
else: raise Prevention
- Treat 200-with-empty-body as skippable
- Fall back to raw cue text
- Monitor for schema drift
When it happens
Trigger: Server returns 200 with an empty envelope, an HTML page, or a schema from a different API version during a multi-cue render.
Common situations: API version drift, gateway interference returning empty JSON, or blank cue text (whitespace-only subtitle lines).
Related errors
- /emotion-enhance returned no data: {resp.text}
- /emotion-enhance failed: status={resp.status_code}, body={re
- /text-to-speech failed: status={resp.status_code}, body={res
- /emotion-enhance failed: status={resp.status_code}, body={re
- /text-to-speech cue {cue.index}: status={resp.status_code},
AI-assisted analysis of babysor/MockingBird@28dc5e14f1 (2026-08-27).
Data as JSON: /api/errors/65a30725eb96b417.
Report an issue: GitHub.