{"record":{"id":"7b78a952cd1900a4","repo":"harry0703/MoneyPrinterTurbo","slug":"llm-provider-returned-invalid-response-content","errorCode":null,"errorMessage":"[{llm_provider}] returned invalid response content","messagePattern":"\\[(.+?)\\] returned invalid response content","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"app/services/llm.py","lineNumber":285,"sourceCode":"                ],\n            )\n\n            try:\n                # 新版 google-genai 通过统一 Client 暴露模型服务。上下文管理器\n                # 会在请求结束后关闭底层 HTTP 连接，避免频繁生成时积累连接资源。\n                with genai.Client(\n                    api_key=api_key,\n                    http_options=http_options,\n                ) as client:\n                    response = client.models.generate_content(\n                        model=model_name,\n                        contents=prompt,\n                        config=generation_config,\n                    )\n                generated_text = response.text\n            except (AttributeError, IndexError, ValueError) as e:\n                logger.warning(f\"gemini returned invalid response content: {str(e)}\")\n                raise ValueError(f\"[{llm_provider}] returned invalid response content\")\n\n            return _normalize_text_response(generated_text, llm_provider)\n\n        if adapter == \"cloudflare_ai_gateway\":\n            account_id = extra_values[\"account_id\"]\n            gateway_id = extra_values[\"gateway_id\"]\n            # Cloudflare 当前推荐的 AI Gateway REST API 兼容 OpenAI SDK。\n            # Account ID 用于构造统一端点，Gateway ID 通过请求头选择；这里\n            # 不再调用 Workers AI 的 /ai/run/{model} 专用接口。\n            client = OpenAI(\n                api_key=api_key,\n                base_url=(\n                    f\"https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/v1\"\n                ),\n                default_headers={\"cf-aig-gateway-id\": gateway_id},\n            )\n            response = client.chat.completions.create(\n                model=model_name,","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/harry0703/MoneyPrinterTurbo/blob/1f9f19c2021a68d04df228f33e9099a0c947f6f8/app/services/llm.py#L267-L303","documentation":"Raised in the gemini adapter branch when reading response.text from google-genai raises AttributeError, IndexError, or ValueError. The google.genai SDK raises ValueError ('response.text' when part candidates lack text) when the model returns no usable text — typically because all candidates were blocked by safety filters or came back empty. The original exception is logged as a warning before re-raising.","triggerScenarios":"client.models.generate_content(...) succeeds HTTP-wise but response.text raises: candidate.finish_reason == SAFETY (prompt blocked), all parts empty (MAX_TOKENS with no output), recitation filter, or response object lacking expected attributes (SDK version mismatch); also prompt rejected by Gemini's content policy.","commonSituations":"Video script/keyword prompts occasionally tripping Gemini safety filters; old google-generativeai vs new google-genai package confusion; max_output_tokens=2048 with thinking models producing empty text; prompts with sensitive words (violence, medical) in the video subject; regional restrictions returning empty candidates.","solutions":["Check the logged warning immediately preceding the raise — it contains the underlying SDK error naming the exact cause (safety block vs empty parts)","If safety-related, rephrase the prompt or lower risk settings; inspect response.candidates[0].finish_reason and safety_ratings in a debug run","For MAX_TOKENS/empty-output, raise max_output_tokens or switch model (e.g. gemini-2.x non-thinking variant)","Verify the google-genai package (not legacy google-generativeai) is installed and matches the SDK this code targets","Retry once via the existing retry loop — transient empty responses do occur"],"exampleFix":"# before\nresponse = client.models.generate_content(model=model_name, contents=prompt, config=generation_config)\ngenerated_text = response.text  # raises ValueError when candidates blocked\n\n# after\nresponse = client.models.generate_content(model=model_name, contents=prompt, config=generation_config)\nif not response.candidates:\n    raise ValueError(f\"[{llm_provider}] no candidates returned (finish_reason={getattr(response.candidates[0], 'finish_reason', 'n/a') if response.candidates else 'none'})\")\ngenerated_text = response.text","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def gemini_has_text(response) -> bool:\n    try:\n        return bool(response and response.text and response.text.strip())\n    except (AttributeError, ValueError, IndexError):\n        return False","tryCatchPattern":"except ValueError as e: — the code already narrows to (AttributeError, IndexError, ValueError) around response.text; callers should retry once (transient safety blocks happen) but rephrase the prompt if a specific subject consistently triggers it","preventionTips":["Pre-check the video subject prompt for obviously sensitive terms before sending","Keep prompts short and neutral — long prompts raise Gemini block probability","Capture the warning log line to identify finish_reason (SAFETY vs empty parts) for each failure"],"tags":["llm","gemini","google-genai","safety-filter","content-moderation"],"backgroundTag":null,"analyzedSha":"1f9f19c2021a68d04df228f33e9099a0c947f6f8","analyzedAt":"2026-08-14T19:41:05.568Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}