{"record":{"id":"6aa39a15cb443ed9","repo":"babysor/MockingBird","slug":"text-to-speech-failed-status-resp-status-code","errorCode":null,"errorMessage":"/text-to-speech failed: status={resp.status_code}, body={resp.text}","messagePattern":"/text-to-speech failed: status=(.+?), body=(.+?)","errorType":"http","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"skills/speak/scripts/noiz_tts.py","lineNumber":117,"sourceCode":"            )\n        }\n    elif not voice_id:\n        raise ValueError(\"Either --voice-id or --reference-audio is required.\")\n\n    try:\n        resp = requests.post(\n            url,\n            headers={\"Authorization\": api_key},\n            data=data,\n            files=files,\n            timeout=timeout,\n        )\n    finally:\n        if files and files[\"file\"][1]:\n            files[\"file\"][1].close()\n\n    if resp.status_code != 200:\n        raise RuntimeError(\n            f\"/text-to-speech failed: status={resp.status_code}, body={resp.text}\"\n        )\n\n    out_path.parent.mkdir(parents=True, exist_ok=True)\n    out_path.write_bytes(resp.content)\n    dur = resp.headers.get(\"X-Audio-Duration\")\n    return float(dur) if dur else -1.0\n\n\ndef main() -> int:\n    parser = argparse.ArgumentParser(description=\"Simple TTS via Noiz API (no timeline).\")\n    g = parser.add_mutually_exclusive_group(required=True)\n    g.add_argument(\"--text\", help=\"Text string to synthesize\")\n    g.add_argument(\"--text-file\", help=\"Path to text file\")\n    parser.add_argument(\"--api-key\", required=True)\n    parser.add_argument(\"--voice-id\")\n    parser.add_argument(\"--reference-audio\", help=\"Local audio for voice cloning\")\n    parser.add_argument(\"--output\", required=True)","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/babysor/MockingBird/blob/28dc5e14f12d7c754612af2fde8e78a4b03f8616/skills/speak/scripts/noiz_tts.py#L99-L135","documentation":"RuntimeError raised by synthesize() when the Noiz /text-to-speech endpoint returns a non-200 status. The full response body is embedded, since causes range from auth failure to invalid voice ids to oversized text.","triggerScenarios":"POST /text-to-speech with bad Authorization (401), unknown voice_id or unsupported output_format (400), missing reference file upload (400), text over the 5000-char limit, or server 5xx.","commonSituations":"Expired API key, typo'd voice id, requesting an output format the account tier doesn't support, or very long text submitted without chunking.","solutions":["Read status/body from the message: 401 → fix key, 400 → check voice_id/format/text length, 5xx → retry later","Verify voice_id is from the current voice listing API","Chunk text to <=5000 chars per request","Retry with exponential backoff on 429/5xx"],"exampleFix":"# before\ntext = open(\"book.txt\").read()\nsynthesize(..., text=text)\n# after\nfor chunk in chunk_text(text, 4500):\n    synthesize(..., text=chunk, out_path=Path(f\"part_{i}.wav\"))","handlingStrategy":"try-catch","validationCode":"assert text and len(text) <= 5000, 'text empty or over 5000 chars'","typeGuard":null,"tryCatchPattern":"try:\n    synthesize(...)\nexcept RuntimeError as e:\n    status = int(str(e).split('status=')[1][:3])\n    if status in (429, *range(500, 600)):\n        time.sleep(2 ** attempt); synthesize(...)\n    else:\n        raise","preventionTips":["Chunk text under 5000 chars","Verify voice_id against the voices listing","Retry only transient statuses"],"tags":["http","api","tts","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"}