{"record":{"id":"419c162343508863","repo":"babysor/MockingBird","slug":"emotion-enhance-failed-status-resp-status-code-419c16","errorCode":null,"errorMessage":"/emotion-enhance failed: status={resp.status_code}, body={resp.text}","messagePattern":"/emotion-enhance failed: status=(.+?), body=(.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"skills/speak/scripts/render_timeline.py","lineNumber":211,"sourceCode":"    _run_ff(cmd)\n\n\n# ── Noiz backend ─────────────────────────────────────────────────────\n\n\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)","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/babysor/MockingBird/blob/28dc5e14f12d7c754612af2fde8e78a4b03f8616/skills/speak/scripts/render_timeline.py#L193-L229","documentation":"Same check as noiz_tts.call_emotion_enhance but in render_timeline's _noiz_emotion_enhance: the Noiz /emotion-enhance endpoint returned a non-200 status and the body is surfaced for diagnosis.","triggerScenarios":"Per-cue emotion enhancement POST failing with 401 (bad key), 429 (rate limit from many cues in a row), 400, or 5xx.","commonSituations":"Rate limiting when enhancing a long timeline of cues rapidly, expired credentials, or service outages mid-render.","solutions":["Add a delay/backoff between per-cue requests to avoid 429","Check credentials and base_url from the message body","Retry the single failing cue rather than the whole render","Skip emotion enhancement (feature flag) if non-essential"],"exampleFix":"# before\nenhanced = _noiz_emotion_enhance(base_url, key, cue.text, timeout)\n# after\ntry:\n    enhanced = _noiz_emotion_enhance(base_url, key, cue.text, timeout)\nexcept RuntimeError:\n    time.sleep(2)\n    enhanced = _noiz_emotion_enhance(base_url, key, cue.text, timeout)","handlingStrategy":"retry","validationCode":"r = requests.get(f\"{base_url}/health\", headers={\"Authorization\": key}, timeout=5)\nassert r.ok","typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    try:\n        return _noiz_emotion_enhance(base_url, key, cue.text, timeout)\n    except RuntimeError:\n        time.sleep(2 ** attempt)\nraise","preventionTips":["Throttle per-cue API calls","Retry transient failures with backoff","Make enhancement optional per config"],"tags":["http","api","rate-limit","noiz-tts","rendering"],"backgroundTag":"http-api-error-response","analyzedSha":"28dc5e14f12d7c754612af2fde8e78a4b03f8616","analyzedAt":"2026-08-27T02:26:53.589Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}