ZhuLinsen/daily_stock_analysis · error · HTTPException
extract_failed
extract_failed
Error message
extract_failed
What it means
Raised by /extract-from-image when extract_stock_codes_from_image raises a ValueError. That service signals expected, caller-facing failures via ValueError (per the endpoint's contract), and its message is passed through verbatim as the 400 detail with error code extract_failed — typical messages include unsupported MIME after content sniffing and 'no vision LLM configured'.
Source
Thrown at api/v1/endpoints/stocks.py:189
logger.warning(f"读取上传文件失败: {e}")
raise HTTPException(
status_code=400,
detail={"error": "read_failed", "message": "读取上传文件失败"},
)
try:
items, raw_text = extract_stock_codes_from_image(data, content_type)
extract_items = [
ExtractItem(code=code, name=name, confidence=conf) for code, name, conf in items
]
codes = [i.code for i in extract_items]
return ExtractFromImageResponse(
codes=codes,
items=extract_items,
raw_text=raw_text if include_raw else None,
)
except ValueError as e:
raise HTTPException(status_code=400, detail={"error": "extract_failed", "message": str(e)})
except Exception as e:
logger.error(f"图片提取失败: {e}", exc_info=True)
raise HTTPException(
status_code=500,
detail={"error": "internal_error", "message": "图片提取失败"},
)
@router.post(
"/parse-import",
response_model=ExtractFromImageResponse,
responses={
200: {"description": "解析结果"},
400: {"description": "未提供数据或解析失败", "model": ErrorResponse},
500: {"description": "服务器错误", "model": ErrorResponse},
},
summary="解析 CSV/Excel/剪贴板",
description="上传 CSV/Excel 文件或粘贴文本,自动解析股票代码。文件上限 2MB,文本上限 100KB。",View on GitHub (pinned to 5159bd72e8)
Solutions
- Read the passthrough message: if it names a missing provider/key, configure one vision LLM credential (Gemini/Anthropic/OpenAI) and retry.
- Verify the payload is a real image: open it locally (PIL.Image.open(...).verify()) before sending.
- Re-download or re-export the source image if it is corrupt.
Example fix
# before
with open('quote.png', 'rb') as f: # actually a renamed PDF
requests.post(url, files={'file': f}) # extract_failed
# after
from PIL import Image
Image.open('quote.png').verify() # raises here -> fix the file first
with open('quote.png', 'rb') as f:
requests.post(url, files={'file': f}) Defensive patterns
Strategy: validation
Validate before calling
from PIL import Image
try:
img = Image.open(io.BytesIO(data)); img.verify()
except Exception:
raise ValueError('文件不是有效图片,请重新导出')
# plus: confirm a vision LLM key is configured before enabling the upload UI Type guard
async function isDecodableImage(bytes: ArrayBuffer): Promise<boolean> {
try { await createImageBitmap(new Blob([bytes])); return true; }
catch { return false; }
} Try / catch
resp = requests.post(url, files={'file': fh})
if resp.status_code == 400:
err = resp.json().get('detail', {})
if err.get('error') == 'extract_failed':
showUserMessage(err.get('message')) # provider/config guidance passthrough
return
raise_for_status_otherwise(resp) Prevention
- Verify the image decodes locally before uploading.
- Check that at least one vision provider key (Gemini/Anthropic/OpenAI) is configured before exposing the feature.
- Do not rely on file extensions — validate magic bytes.
When it happens
Trigger: Posting a file whose declared Content-Type is allowed but whose actual bytes are not a decodable image (corrupt or misnamed file); running the API without any vision-capable LLM credentials (Gemini/Anthropic/OpenAI) so the extractor cannot be constructed; the vision provider returning content the parser rejects.
Common situations: A .png that is actually a renamed PDF or text file; missing/invalid API keys in env for all three vision providers; truncated downloads; zero-byte uploads that pass the earlier checks.
Related errors
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/a0b59dcef4d2955c.
Report an issue: GitHub.