{"record":{"id":"1eda6b9aa77dd213","repo":"ZhuLinsen/daily_stock_analysis","slug":"share-image-unavailable","errorCode":"share_image_unavailable","errorMessage":"分享图片生成失败，请检查 {engine} 转图工具是否已安装并可用","messagePattern":"分享图片生成失败，请检查 (.+?) 转图工具是否已安装并可用","errorType":"http","errorClass":"HTTPException","httpStatus":503,"severity":"error","filePath":"api/v1/endpoints/history.py","lineNumber":895,"sourceCode":"    },\n    summary=\"生成历史报告分享图片\",\n    description=\"根据历史报告 Markdown 与持久化结构化数据生成确定性的 PNG 分享图片\",\n)\ndef get_history_share_image(\n    record_id: str,\n    db_manager: DatabaseManager = Depends(get_database_manager),\n) -> Response:\n    result, markdown_content = _history_share_image_input(record_id, db_manager)\n\n    config = get_config()\n    image_bytes = markdown_to_image(\n        markdown_content,\n        max_chars=getattr(config, \"markdown_to_image_max_chars\", 15000),\n        structured_payload=_history_share_image_payload(result),\n    )\n    if image_bytes is None:\n        engine = getattr(config, \"md2img_engine\", \"wkhtmltoimage\")\n        raise HTTPException(\n            status_code=503,\n            detail={\n                \"error\": \"share_image_unavailable\",\n                \"message\": f\"分享图片生成失败，请检查 {engine} 转图工具是否已安装并可用\",\n            },\n        )\n\n    filename = f\"dsa-report-{result.get('id') or record_id}.png\"\n    return Response(\n        content=image_bytes,\n        media_type=\"image/png\",\n        headers={\n            \"Content-Disposition\": f'attachment; filename=\"{filename}\"',\n            \"Cache-Control\": \"no-store\",\n            \"X-Content-Type-Options\": \"nosniff\",\n        },\n    )\n","sourceCodeStart":877,"sourceCodeEnd":913,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/api/v1/endpoints/history.py#L877-L913","documentation":"503 share_image_unavailable from GET /history/{record_id}/share-image (the PNG endpoint, distinct from the HTML one). The handler calls markdown_to_image(...) which shells out to an external converter — the engine named by config.md2img_engine, default 'wkhtmltoimage'. When markdown_to_image returns None (engine binary missing, not executable, or failed to run), the endpoint returns 503 telling you to install/check the {engine} tool. It is an environment problem, not a data problem.","triggerScenarios":"wkhtmltoimage (or the configured md2img_engine) not installed in the container/host; binary present but not on PATH for the server process; missing runtime dependencies of the engine (X11 libs for wkhtmltoimage in slim Docker images); engine crashing on specific input so the wrapper returns None.","commonSituations":"Docker/slim deployments that never installed the converter; macOS/Windows local runs where the binary name differs; CI environments lacking the tool; PATH differences between the shell used to install and the service process.","solutions":["Install the engine named in the message: e.g. apt-get install -y wkhtmltoimage (or xfonts + libxrender1 for headless rendering in slim images).","Verify the server process can find it: `which wkhtmltoimage` under the same user/env the API runs.","Alternatively switch config md2img_engine to an available engine supported by markdown_to_image and restart.","In Docker-based deployments, add the install step to the image build rather than the running container."],"exampleFix":"# before: Dockerfile with no converter\nRUN pip install -r requirements.txt\n\n# after\nRUN apt-get update && apt-get install -y --no-install-recommends wkhtmltopdf xvfb libxrender1 fontconfig xfonts-base \\\n    && rm -rf /var/lib/apt/lists/*\nRUN pip install -r requirements.txt","handlingStrategy":"fallback","validationCode":"// capability probe before offering PNG share\nasync function engineAvailable() {\n  try { await fetchPngShare(knownSmallReportId); return true; }\n  catch (e) { return !isHttp503(e); }\n}","typeGuard":null,"tryCatchPattern":"try { png = await getPngShare(id); }\ncatch (e) {\n  if (isHttp503(e) && e.code === 'share_image_unavailable') {\n    png = await renderLocally(await getShareHtml(id)); // desktop-side screenshot fallback\n  }\n}","preventionTips":["Bake the image engine (e.g. wkhtmltoimage plus its X/font deps) into the deployment image.","Prefer the /share-image-html endpoint with client-side rendering when the engine cannot be installed.","Alert on 503s — they indicate environment drift, not data problems."],"tags":["http-503","share-image","environment","external-tool"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}