{"record":{"id":"35ef542c5e8af720","repo":"ZhuLinsen/daily_stock_analysis","slug":"not-found-35ef54","errorCode":"not_found","errorMessage":"未找到 id/query_id={record_id} 的分析记录","messagePattern":"未找到 id/query_id=(.+?) 的分析记录","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"warning","filePath":"api/v1/endpoints/history.py","lineNumber":97,"sourceCode":"        if isinstance(context_snapshot, Mapping):\n            market_payload = context_snapshot.get(\"market_review_payload\")\n            if isinstance(market_payload, Mapping):\n                return market_payload\n\n    raw_result = result.get(\"raw_result\")\n    return raw_result if isinstance(raw_result, Mapping) else None\n\n\ndef _history_share_image_input(\n    record_id: str,\n    db_manager: DatabaseManager,\n) -> tuple[Mapping[str, Any], str]:\n    \"\"\"Load the shared persisted input used by PNG and desktop HTML renderers.\"\"\"\n\n    service = HistoryService(db_manager)\n    result = service.resolve_and_get_detail(record_id)\n    if result is None:\n        raise HTTPException(\n            status_code=404,\n            detail={\n                \"error\": \"not_found\",\n                \"message\": f\"未找到 id/query_id={record_id} 的分析记录\",\n            },\n        )\n\n    try:\n        markdown_content = service.get_markdown_report(record_id)\n    except MarkdownReportGenerationError as exc:\n        logger.error(\"Share image report generation failed for %s: %s\", record_id, exc.message)\n        raise HTTPException(\n            status_code=500,\n            detail={\n                \"error\": \"generation_failed\",\n                \"message\": f\"生成分享图片所需报告失败: {exc.message}\",\n            },\n        ) from exc","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/api/v1/endpoints/history.py#L79-L115","documentation":"The history share-image pipeline resolves a record by id or query_id via HistoryService.resolve_and_get_detail; a None result raises HTTP 404 code=not_found '未找到 id/query_id={record_id} 的分析记录' (history.py:97-105). This helper feeds both the PNG and desktop-HTML share renderers, so any share URL with an unresolvable record fails here before rendering.","triggerScenarios":"GET share-image endpoints where record_id is neither a numeric history id nor a known query_id; record deleted between listing and share-image fetch; wrong DB (record created in another environment); id typos or truncated UUIDs in hand-built share links.","commonSituations":"Desktop/Web share links pointing at a server with a different database (e.g. local vs deployed); user deletes history then opens an old share URL; records pruned by retention cleanup; copy/paste corruption of the query_id part of share URLs.","solutions":["Verify the record exists first: GET the history detail endpoint for the same record_id and confirm a 200","If sharing is expected to outlive deletions, export/render the image before deleting the record, or persist the rendered artifact separately","Align environments: ensure the share URL targets the same server/DB that produced the record id","Sanitize record_id client-side (numeric id or full query_id) before building share links"],"exampleFix":"# before\nimg = requests.get(f\"{base}/history/{record_id}/share-image\")\nimg.raise_for_status()\n\n# after\ndetail = requests.get(f\"{base}/history/{record_id}\")\nif detail.status_code == 404:\n    raise FileNotFoundError(f\"history record {record_id} gone; cannot share\")\nimg = requests.get(f\"{base}/history/{record_id}/share-image\")\nimg.raise_for_status()","handlingStrategy":"validation","validationCode":"# Resolve via the detail endpoint before requesting the share artifact\nimport requests\n\ndef ensure_shareable(record_id: str) -> dict:\n    r = requests.get(f\"{BASE}/api/v1/history/{record_id}\")\n    if r.status_code == 404:\n        raise FileNotFoundError(f\"record {record_id} no longer exists\")\n    r.raise_for_status()\n    return r.json()","typeGuard":"def is_history_record_missing(resp) -> bool:\n    return (\n        resp.status_code == 404\n        and isinstance(resp.json().get('detail'), dict)\n        and resp.json()['detail'].get('error') == 'not_found'\n        and '分析记录' in resp.json()['detail'].get('message', '')\n    )","tryCatchPattern":"try:\n    img = get_share_image(record_id)\nexcept HTTPError as e:\n    if is_history_record_missing(e.response):\n        invalidate_share_link(record_id)  # stop offering a dead share\n    else:\n        raise","preventionTips":["Render/persist the share artifact at share time rather than re-resolving records later","Validate record_id shape (numeric id or full query_id) before building share URLs","Keep share links pointed at the same environment/DB that created the record"],"tags":["history","http-404","share-image","record-not-found"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}