{"record":{"id":"4bc928acbebbf6fd","repo":"opendatalab/MinerU","slug":"detail","errorCode":null,"errorMessage":"{detail}","messagePattern":"\\{detail\\}","errorType":"http","errorClass":"HTTPException","httpStatus":null,"severity":"error","filePath":"mineru/cli/router.py","lineNumber":1320,"sourceCode":"                \"GET\",\n                result_url,\n                timeout=build_result_download_timeout(),\n            ),\n            stream=True,\n        )\n    except httpx.HTTPError as exc:\n        await request.app.state.router_task_registry.increment_upstream_error(task.task_id, str(exc))\n        raise HTTPException(status_code=502, detail=str(exc)) from exc\n\n    if upstream_response.status_code != 200:\n        body = await upstream_response.aread()\n        await upstream_response.aclose()\n        detail = body.decode(\"utf-8\", errors=\"replace\").strip() or upstream_response.reason_phrase\n        await request.app.state.router_task_registry.increment_upstream_error(\n            task.task_id,\n            f\"{upstream_response.status_code} {detail}\",\n        )\n        raise HTTPException(\n            status_code=upstream_response.status_code,\n            detail=detail,\n        )\n\n    content_type = upstream_response.headers.get(\"content-type\", \"\")\n    if \"application/json\" in content_type:\n        body = await upstream_response.aread()\n        await upstream_response.aclose()\n        return Response(\n            content=body,\n            status_code=200,\n            media_type=\"application/json\",\n        )\n\n    headers: dict[str, str] = {}\n    content_disposition = upstream_response.headers.get(\"content-disposition\")\n    if content_disposition:\n        headers[\"content-disposition\"] = content_disposition","sourceCodeStart":1302,"sourceCodeEnd":1338,"githubUrl":"https://github.com/opendatalab/MinerU/blob/4fe4bde114a23ee5dd637eae99b767f4669bf58c/mineru/cli/router.py#L1302-L1338","documentation":"HTTPException that re-raises the upstream's non-200 status and body verbatim when fetching a task result. The upstream response body (or reason phrase if empty) becomes the error detail, and the failure is recorded on the task record.","triggerScenarios":"GET {upstream}/tasks/{id}/result returns e.g. 404 (result evicted upstream), 403, or 500; the router reads the body, closes the stream, and returns the same status/detail to the client.","commonSituations":"Upstream restarted and lost the finished result; result retention on the upstream expired; upstream error during result assembly. The status code is upstream's, so it identifies the true cause.","solutions":["Read the status code and detail — they come straight from the upstream result endpoint","If 404 upstream: the result no longer exists there (restart/eviction); re-submit the task","Check upstream logs for the matching request; fix upstream result retention/storage if results vanish"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"r = client.get(f\"{router}/tasks/{task_id}/result\")\nif r.status_code == 404:\n    resubmit(files)  # upstream lost the result; nothing to fetch\nelif r.status_code >= 500:\n    retry_with_backoff(lambda: client.get(f\"{router}/tasks/{task_id}/result\"))","preventionTips":["Archive results client-side immediately after download; upstream result storage is transient","Distinguish upstream-originated statuses (pass-through) from router 502s: retry only transient classes","Enable durable result storage on the upstream if restarts are frequent"],"tags":["mineru","router","upstream","result-download","pass-through"],"backgroundTag":null,"analyzedSha":"4fe4bde114a23ee5dd637eae99b767f4669bf58c","analyzedAt":"2026-08-14T21:29:18.456Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}