{"record":{"id":"b37263777ba752ae","repo":"ZhuLinsen/daily_stock_analysis","slug":"image-too-large-max-max-size-bytes-1024-10","errorCode":null,"errorMessage":"Image too large (max {MAX_SIZE_BYTES // (1024 * 1024)}MB)","messagePattern":"Image too large \\(max (.+?)MB\\)","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"src/services/image_stock_extractor.py","lineNumber":373,"sourceCode":"    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 响应。\n\n    Raises:\n        ValueError: 图片无效、未配置 Vision API 或提取失败时。\n    \"\"\"\n    mime_type = (mime_type or \"image/jpeg\").strip().lower().split(\";\")[0].strip()\n    if mime_type not in ALLOWED_MIME:\n        raise ValueError(f\"不支持的图片类型: {mime_type}。允许: {list(ALLOWED_MIME)}\")\n\n    if not image_bytes:\n        raise ValueError(\"图片内容为空\")\n\n    if len(image_bytes) > MAX_SIZE_BYTES:\n        raise ValueError(f\"Image too large (max {MAX_SIZE_BYTES // (1024 * 1024)}MB)\")\n\n    _verify_image_magic_bytes(image_bytes, mime_type)\n\n    image_b64 = base64.b64encode(image_bytes).decode(\"ascii\")\n    model = _resolve_vision_model()\n    keys = _get_api_keys_for_model(model, get_config())\n\n    last_error: Optional[Exception] = None\n    for attempt in range(3):\n        try:\n            key = random.choice(keys) if keys else None\n            raw = _call_litellm_vision(image_b64, mime_type, api_key=key)\n            logger.debug(\"[ImageExtractor] raw LLM response:\\n%s\", raw)\n            items = _parse_items_from_text(raw)\n            logger.info(\n                f\"[ImageExtractor] {model} 提取 {len(items)} 个: \"\n                f\"{[(i[0], i[1]) for i in items[:5]]}{'...' if len(items) > 5 else ''}\"\n            )","sourceCodeStart":355,"sourceCodeEnd":391,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/services/image_stock_extractor.py#L355-L391","documentation":"Guard in extract_stock_codes_from_image: len(image_bytes) exceeds MAX_SIZE_BYTES (5MB). Checked before magic-byte verification and base64 encoding, because base64 inflates payload ~33% and vision APIs have request-size limits.","triggerScenarios":"Uploading a photo/screenshot larger than 5MB (common with modern phone photos and high-res screenshots).","commonSituations":"Direct phone photo uploads; PNG screenshots of multi-monitor setups; no client-side size limit enforced.","solutions":["Compress/resize client-side before upload (canvas resize or quality reduction to JPEG)","If high fidelity needed, downscale to ~2000px and re-encode as JPEG — vision models don't need full resolution","Enforce a 5MB limit in the upload UI so the error never reaches the backend"],"exampleFix":"# before\nitems, raw = extract_stock_codes_from_image(open('huge.png','rb').read(), 'image/png')  # >5MB\n# after: downscale + re-encode\nfrom PIL import Image\nim = Image.open('huge.png'); im.thumbnail((2000, 2000))\nbuf = io.BytesIO(); im.convert('RGB').save(buf, 'JPEG', quality=85)\nitems, raw = extract_stock_codes_from_image(buf.getvalue(), 'image/jpeg')","handlingStrategy":"validation","validationCode":"from src.services.image_stock_extractor import MAX_SIZE_BYTES\nif len(image_bytes) > MAX_SIZE_BYTES:\n    image_bytes, mime = downscale_and_reencode(image_bytes, max_px=2000)","typeGuard":"def within_image_limit(b: bytes) -> bool:\n    return len(b) <= 5 * 1024 * 1024","tryCatchPattern":null,"preventionTips":["Set the upload form's max file size to 5MB with client feedback","Always downscale photos to <=2000px JPEG before sending","Remember base64 adds ~33% — stay well under the raw-bytes limit"],"tags":["validation","image","size-limit"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}