{"record":{"id":"4890ab4849571bf9","repo":"HKUDS/DeepTutor","slug":"failed-to-upload-pdf-to-mineru-exc","errorCode":null,"errorMessage":"Failed to upload PDF to MinerU: {exc}","messagePattern":"Failed to upload PDF to MinerU: (.+?)","errorType":"exception","errorClass":"MinerUError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/parsing/engines/mineru/cloud.py","lineNumber":156,"sourceCode":"    file_urls = data.get(\"file_urls\") or []\n    if not batch_id or not isinstance(file_urls, list) or not file_urls:\n        raise MinerUError(\"MinerU API did not return an upload URL (missing batch_id/file_urls).\")\n    return batch_id, str(file_urls[0])\n\n\ndef _upload_file(pdf_path: Path, upload_url: str) -> None:\n    \"\"\"PUT the PDF bytes to the signed URL.\n\n    The signed URL carries its own auth; per MinerU's docs we must NOT send an\n    ``Authorization`` or ``Content-Type`` header (a stray Content-Type breaks\n    the OSS signature).\n    \"\"\"\n    data = pdf_path.read_bytes()\n    try:\n        response = httpx.put(upload_url, content=data, timeout=_UPLOAD_TIMEOUT_SECONDS)\n        response.raise_for_status()\n    except httpx.HTTPError as exc:\n        raise MinerUError(f\"Failed to upload PDF to MinerU: {exc}\") from exc\n\n\ndef _poll_for_zip(\n    client: httpx.Client,\n    batch_id: str,\n    file_name: str,\n    *,\n    key_pool: KeyPool,\n    poll_interval: float,\n    timeout: float,\n    on_progress: Callable[[str], None] | None = None,\n) -> str:\n    \"\"\"Poll the batch results until our file is ``done``; return full_zip_url.\"\"\"\n    deadline = time.monotonic() + timeout\n    last_state = \"\"\n    last_report = \"\"\n    while True:\n        payload = _get_json(client, f\"/api/v4/extract-results/batch/{batch_id}\", key_pool)","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/parsing/engines/mineru/cloud.py#L138-L174","documentation":"The signed upload URL was obtained, but the HTTP PUT of the PDF bytes failed (network error, DNS, TLS, timeout, or non-2xx status via raise_for_status).","triggerScenarios":"httpx.put(upload_url, ...) raising httpx.HTTPError — expired signed URL, network drop, proxy blocking the object-store host, or 4xx/5xx from storage.","commonSituations":"Long delay between requesting the batch and uploading (URL expiry), corporate proxies/firewalls, large PDFs exceeding an upload limit or timing out (_UPLOAD_TIMEOUT_SECONDS).","solutions":["Retry the whole parse (a fresh signed URL is requested each attempt).","Check network/proxy access to the storage host in the upload URL.","Reduce PDF size or check the timeout budget for large files.","Verify system clock — signed URLs are time-sensitive."],"exampleFix":"# before\nres = parse_pdf_to_workdir(pdf, wd, cfg)\n\n# after\nfor attempt in range(3):\n    try:\n        res = parse_pdf_to_workdir(pdf, wd, cfg); break\n    except MinerUError as e:\n        if \"upload\" not in str(e).lower() or attempt == 2: raise\n        time.sleep(2 ** attempt)","handlingStrategy":"retry","validationCode":"def uploadable(pdf: Path) -> bool:\n    return pdf.is_file() and pdf.stat().st_size < 200 * 1024 * 1024","typeGuard":null,"tryCatchPattern":"for i in range(3):\n    try:\n        parse_pdf_to_workdir(pdf, wd, cfg); break\n    except MinerUError as e:\n        if \"upload PDF\" not in str(e) or i == 2: raise\n        time.sleep(2 ** i)","preventionTips":["Upload promptly after the batch is created (signed URLs expire).","Compress/split very large PDFs.","Ensure egress to the object-storage host."],"tags":["mineru","cloud","upload","network"],"backgroundTag":"file-upload-failed","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}