{"record":{"id":"65a30725eb96b417","repo":"babysor/MockingBird","slug":"emotion-enhance-returned-no-data-resp-text-65a307","errorCode":null,"errorMessage":"/emotion-enhance returned no data: {resp.text}","messagePattern":"/emotion-enhance returned no data: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"warning","filePath":"skills/speak/scripts/render_timeline.py","lineNumber":216,"sourceCode":"\ndef _noiz_emotion_enhance(\n    base_url: str, api_key: str, text: str, timeout: int\n) -> str:\n    import requests  # noqa: delayed import so kokoro path doesn't need requests\n\n    resp = requests.post(\n        f\"{base_url.rstrip('/')}/emotion-enhance\",\n        headers={\"Authorization\": api_key, \"Content-Type\": \"application/json\"},\n        json={\"text\": text},\n        timeout=timeout,\n    )\n    if resp.status_code != 200:\n        raise RuntimeError(\n            f\"/emotion-enhance failed: status={resp.status_code}, body={resp.text}\"\n        )\n    enhanced = resp.json().get(\"data\", {}).get(\"emotion_enhance\")\n    if not enhanced:\n        raise RuntimeError(f\"/emotion-enhance returned no data: {resp.text}\")\n    return enhanced\n\n\ndef _bool_form(v: Any) -> str:\n    return \"true\" if bool(v) else \"false\"\n\n\ndef _resolve_reference_audio(ref: str, timeout: int) -> Tuple[Path, Optional[Path]]:\n    \"\"\"Resolve reference_audio to a path. If ref is a URL, download to temp file.\n    Returns (path_to_use, temp_path_to_cleanup_or_None).\"\"\"\n    if ref.startswith(\"http://\") or ref.startswith(\"https://\"):\n        import requests\n        tmp = tempfile.NamedTemporaryFile(suffix=\".wav\", delete=False)\n        tmp.close()\n        r = requests.get(ref, timeout=timeout)\n        r.raise_for_status()\n        Path(tmp.name).write_bytes(r.content)\n        return Path(tmp.name), Path(tmp.name)","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/babysor/MockingBird/blob/28dc5e14f12d7c754612af2fde8e78a4b03f8616/skills/speak/scripts/render_timeline.py#L198-L234","documentation":"Raised when /emotion-enhance answers 200 but data.emotion_enhance is missing or falsy in the JSON — a response-shape mismatch during timeline rendering.","triggerScenarios":"Server returns 200 with an empty envelope, an HTML page, or a schema from a different API version during a multi-cue render.","commonSituations":"API version drift, gateway interference returning empty JSON, or blank cue text (whitespace-only subtitle lines).","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"],"exampleFix":"# before\nenhanced = resp.json().get(\"data\", {}).get(\"emotion_enhance\")\nif not enhanced:\n    raise RuntimeError(...)\n# after\nenhanced = resp.json().get(\"data\", {}).get(\"emotion_enhance\") or cue.text","handlingStrategy":"fallback","validationCode":"if not cue.text.strip():\n    continue  # skip empty cues before enhancement","typeGuard":null,"tryCatchPattern":"try:\n    enhanced = _noiz_emotion_enhance(...)\nexcept RuntimeError as e:\n    if 'no data' in str(e): enhanced = cue.text\n    else: raise","preventionTips":["Treat 200-with-empty-body as skippable","Fall back to raw cue text","Monitor for schema drift"],"tags":["http","schema","api","noiz-tts"],"backgroundTag":"unexpected-api-response-schema","analyzedSha":"28dc5e14f12d7c754612af2fde8e78a4b03f8616","analyzedAt":"2026-08-27T02:26:53.589Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}