{"record":{"id":"4b1307cd86872902","repo":"harry0703/MoneyPrinterTurbo","slug":"elevenlabs-api-key-is-required","errorCode":null,"errorMessage":"ElevenLabs API key is required","messagePattern":"ElevenLabs API key is required","errorType":"exception","errorClass":"ElevenLabsAuthenticationError","httpStatus":null,"severity":"error","filePath":"app/services/elevenlabs_music.py","lineNumber":114,"sourceCode":"        )\n    else:\n        body = str(body_bytes)\n    body = body.strip().replace(\"\\n\", \" \")[:MAX_ERROR_BODY_BYTES]\n    return body or response.reason or \"request failed\"\n\n\ndef test_connection() -> dict[str, Any]:\n    \"\"\"\n    使用不消耗音乐生成额度的订阅接口检查 API Key 和账号套餐。\n\n    该接口只能确认 Key 可访问订阅信息以及账号不是免费套餐，不能证明当前 Key\n    一定拥有 Music endpoint 权限。ElevenLabs 允许按 endpoint、额度和 IP 限制\n    Key，因此 UI 成功提示必须保留这一边界，实际权限仍由生成请求最终确认。\n    响应中的账单和用量详情不会写入日志，避免记录账号隐私。\n    \"\"\"\n    api_key = get_api_key()\n    if not api_key:\n        raise ElevenLabsAuthenticationError(\"ElevenLabs API key is required\")\n    try:\n        with requests.get(\n            f\"{_base_url()}{SUBSCRIPTION_PATH}\",\n            headers={\"xi-api-key\": api_key},\n            timeout=(15, 30),\n            stream=True,\n        ) as response:\n            if response.status_code == 401:\n                raise ElevenLabsAuthenticationError(\n                    \"ElevenLabs API key was rejected (401): \"\n                    f\"{_safe_response_error(response)}\"\n                )\n            if not response.ok:\n                raise ElevenLabsMusicError(\n                    \"ElevenLabs account check failed \"\n                    f\"({response.status_code}): \"\n                    f\"{_safe_response_error(response)}\"\n                )","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/harry0703/MoneyPrinterTurbo/blob/1f9f19c2021a68d04df228f33e9099a0c947f6f8/app/services/elevenlabs_music.py#L96-L132","documentation":"ElevenLabsAuthenticationError raised by test_connection() when get_api_key() returns empty. The key is read from config.elevenlabs['api_key'] first, with the ELEVENLABS_API_KEY environment variable as fallback; both were blank/unset. Music generation shares one key with ElevenLabs TTS, so no separate music key exists to configure.","triggerScenarios":"Calling test_connection() (or any path calling it, e.g. validate_generation_access) with neither the elevenlabs.api_key config entry nor the ELEVENLABS_API_KEY env var set to a non-empty value.","commonSituations":"Fresh install without config.toml entry; env var set in the dev shell but not in the Docker container/service unit; key accidentally saved with surrounding quotes making it a blank string after strip().","solutions":["Set elevenlabs.api_key in the app config (same key as TTS) or export ELEVENLABS_API_KEY in the process environment.","For Docker deployments, confirm the variable reaches the container (docker exec env | grep ELEVENLABS) and is not only in the host shell.","Check with is_enabled() before offering the ElevenLabs music option in the UI."],"exampleFix":"# before: no key configured\ntest_connection()  # -> ElevenLabsAuthenticationError\n\n# after\nexport ELEVENLABS_API_KEY='xi-...'\ntest_connection()","handlingStrategy":"validation","validationCode":"from app.services import elevenlabs_music\n\nif not elevenlabs_music.is_enabled():\n    raise ConfigError('set elevenlabs.api_key or ELEVENLABS_API_KEY before testing')","typeGuard":null,"tryCatchPattern":"try:\n    elevenlabs_music.test_connection()\nexcept ElevenLabsAuthenticationError as e:\n    if 'is required' in str(e):\n        prompt_user_for_api_key()  # then retry","preventionTips":["Gate the ElevenLabs music UI on is_enabled() so users never reach a missing-key error.","Keep the key in config.elevenlabs (shared with TTS) so one entry covers both features.","For deployments, inject ELEVENLABS_API_KEY via the container/service environment."],"tags":["elevenlabs","api-key","authentication","config"],"backgroundTag":null,"analyzedSha":"1f9f19c2021a68d04df228f33e9099a0c947f6f8","analyzedAt":"2026-08-14T19:41:05.568Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}