{"record":{"id":"864c92b89408e6dd","repo":"ZhuLinsen/daily_stock_analysis","slug":"litellm-vision-returned-empty-response","errorCode":null,"errorMessage":"LiteLLM vision returned empty response","messagePattern":"LiteLLM vision returned empty response","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"src/services/image_stock_extractor.py","lineNumber":342,"sourceCode":"        call_kwargs[\"api_key\"] = effective_api_key\n    if deployment_params.get(\"api_base\"):\n        call_kwargs[\"api_base\"] = deployment_params[\"api_base\"]\n    if deployment_params.get(\"extra_headers\"):\n        call_kwargs[\"extra_headers\"] = dict(deployment_params[\"extra_headers\"])\n    # Add api_base and custom headers for OpenAI-compatible providers\n    if not deployment_params and not model.startswith(\"gemini/\") and not model.startswith(\"anthropic/\") and not model.startswith(\"vertex_ai/\"):\n        if cfg.openai_base_url:\n            call_kwargs[\"api_base\"] = cfg.openai_base_url\n        if cfg.openai_base_url and \"aihubmix.com\" in cfg.openai_base_url:\n            call_kwargs[\"extra_headers\"] = {\"APP-Code\": \"GPIJ3886\"}\n\n    if getattr(litellm, \"completion\", None) is None:\n        import litellm as litellm_module\n        litellm = litellm_module\n    response = litellm.completion(**call_kwargs)\n    if response and response.choices and response.choices[0].message.content:\n        return response.choices[0].message.content\n    raise ValueError(\"LiteLLM vision returned empty response\")\n\n\ndef extract_stock_codes_from_image(\n    image_bytes: bytes,\n    mime_type: str,\n) -> Tuple[List[Tuple[str, Optional[str], str]], str]:\n    \"\"\"\n    从图片中提取股票代码及名称（使用 Vision LLM）。\n\n    优先级：Gemini -> Anthropic -> OpenAI（首个可用）。\n    支持多 Key 轮询与重试（最多 3 次，指数退避）。\n\n    Args:\n        image_bytes: 原始图片字节\n        mime_type: MIME 类型（如 image/jpeg, image/png）\n\n    Returns:\n        (items, raw_text) - items 为 [(code, name?, confidence), ...]，raw_text 为原始 LLM 响应。","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/services/image_stock_extractor.py#L324-L360","documentation":"Raised after a successful litellm.completion() call when the response has no choices, an empty first message, or empty content. The HTTP call itself succeeded, but nothing text-like came back, so the extractor cannot parse stock codes.","triggerScenarios":"Provider returns 200 with empty content (safety filter, finish_reason=length/content_filter, tool-call-only response, or a misrouted proxy model returning an object without message content).","commonSituations":"Image flagged by provider content filters; model alias on a proxy (e.g. aihubmix) resolving to a non-vision model; truncated response due to max_tokens; flaky provider returning malformed payloads.","solutions":["Retry the call — transient empty responses often succeed on retry (the outer loop already retries 3x, so persistent failure means provider/config issue)","Log response.choices[0].finish_reason and full response to see why content is empty","Verify the wire model actually supports vision input (check deployment_params['model'])","If content_filter, change image or use a different provider/model"],"exampleFix":"// not applicable — runtime/provider issue, no caller-side code fix","handlingStrategy":"retry","validationCode":null,"typeGuard":"def has_content(resp) -> bool:\n    try:\n        return bool(resp and resp.choices and resp.choices[0].message.content)\n    except AttributeError:\n        return False","tryCatchPattern":"for attempt in range(3):\n    try:\n        return _call_litellm_vision(b64, mime, key)\n    except ValueError as e:\n        if 'empty response' not in str(e) or attempt == 2:\n            raise\n        time.sleep(2 ** attempt)","preventionTips":["Log finish_reason and raw response on empty content to distinguish filter vs truncation","Set explicit max_tokens high enough to avoid finish_reason=length","Verify the wire model supports vision before enabling image import"],"tags":["litellm","vision","empty-response","provider"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}