{"record":{"id":"aff024bf2abc0f97","repo":"babysor/MockingBird","slug":"text-to-speech-cue-cue-index-status-resp-stat","errorCode":null,"errorMessage":"/text-to-speech cue {cue.index}: status={resp.status_code}, body={resp.text}","messagePattern":"/text-to-speech cue (.+?): status=(.+?), body=(.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"skills/speak/scripts/render_timeline.py","lineNumber":298,"sourceCode":"        }\n    elif not cfg.get(\"voice_id\"):\n        raise ValueError(\n            f\"Cue {cue.index}: either voice_id or reference_audio required.\"\n        )\n\n    try:\n        resp = requests.post(\n            url, headers={\"Authorization\": api_key},\n            data=payload, files=files, timeout=timeout,\n        )\n    finally:\n        if files and files[\"file\"][1]:\n            files[\"file\"][1].close()\n        if ref_cleanup is not None:\n            ref_cleanup.unlink(missing_ok=True)\n\n    if resp.status_code != 200:\n        raise RuntimeError(\n            f\"/text-to-speech cue {cue.index}: \"\n            f\"status={resp.status_code}, body={resp.text}\"\n        )\n    out_path.write_bytes(resp.content)\n    dur_h = resp.headers.get(\"X-Audio-Duration\")\n    return float(dur_h) if dur_h else -1.0\n\n\n# ── Kokoro backend ───────────────────────────────────────────────────\n\n\ndef _ensure_kokoro() -> None:\n    if not shutil.which(\"kokoro-tts\"):\n        raise RuntimeError(\"kokoro-tts CLI not found.\")\n\n\ndef _kokoro_tts(\n    cue: Cue,","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/babysor/MockingBird/blob/28dc5e14f12d7c754612af2fde8e78a4b03f8616/skills/speak/scripts/render_timeline.py#L280-L316","documentation":"RuntimeError raised in _noiz_tts when the per-cue /text-to-speech request returns non-200. The cue index and response body identify which subtitle line failed and why, so a multi-cue render pinpoints the failure.","triggerScenarios":"A cue with text over the char limit, invalid voice_id/reference for that cue, auth failure, or rate limiting after many sequential requests.","commonSituations":"One overly long subtitle line, a voice-map entry with an expired/invalid voice id, or 429s during a large timeline render.","solutions":["Use the cue index in the message to find the offending subtitle line","Apply the body's guidance: shorten text, fix voice config, or back off for 429","Add per-cue retry with backoff","Cache successful cues so reruns skip completed ones"],"exampleFix":"# before\nseg = _noiz_tts(cue, cfg, fmt, ...)\n# after\nfor attempt in range(3):\n    try:\n        seg = _noiz_tts(cue, cfg, fmt, ...)\n        break\n    except RuntimeError:\n        if attempt == 2: raise\n        time.sleep(2 ** attempt)","handlingStrategy":"retry","validationCode":"assert len(cue.text) <= 5000, f'cue {cue.index} too long'","typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    try:\n        seg = _noiz_tts(cue, cfg, fmt, ...); break\n    except RuntimeError as e:\n        if attempt == 2 or 'status=4' in str(e) and '429' not in str(e): raise\n        time.sleep(2 ** attempt)","preventionTips":["Retry per-cue with backoff; skip-and-report strategy for batch renders","Cache rendered segments for resume","Watch the cue index in errors"],"tags":["http","api","tts","rendering","noiz-tts"],"backgroundTag":"http-api-error-response","analyzedSha":"28dc5e14f12d7c754612af2fde8e78a4b03f8616","analyzedAt":"2026-08-27T02:26:53.589Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}