{"record":{"id":"4b800615ba0c4d21","repo":"HKUDS/DeepTutor","slug":"pdf-file-not-found-pdf-path","errorCode":null,"errorMessage":"PDF file not found: {pdf_path}","messagePattern":"PDF file not found: (.+?)","errorType":"exception","errorClass":"MinerUError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/parsing/engines/mineru/cloud.py","lineNumber":77,"sourceCode":"    timeout: float = DEFAULT_TIMEOUT_SECONDS,\n    on_progress: Callable[[str], None] | None = None,\n) -> Path:\n    \"\"\"Parse ``pdf_path`` via the MinerU cloud API; return the working dir.\n\n    The working dir sits under ``output_base`` (named after the PDF stem) and\n    holds the unzipped MinerU artifacts. ``on_progress`` (if given) receives a\n    short status line whenever the polled task state / page count changes.\n    Raises :class:`MinerUError` on any misconfiguration, API error, timeout,\n    or extraction failure.\n    \"\"\"\n    if not config.api_keys:\n        raise MinerUError(\n            \"MinerU cloud mode is selected but no API token is configured. \"\n            \"Add a token in Settings → MinerU, or switch to local mode.\"\n        )\n    pdf_path = Path(pdf_path)\n    if not pdf_path.is_file():\n        raise MinerUError(f\"PDF file not found: {pdf_path}\")\n\n    base_url = config.api_base_url.rstrip(\"/\")\n    key_pool = KeyPool(config.api_keys)\n\n    def report(message: str) -> None:\n        if on_progress is None:\n            return\n        try:\n            on_progress(message)\n        except Exception:\n            logger.debug(\"on_progress callback failed\", exc_info=True)\n\n    with httpx.Client(base_url=base_url, headers={\"Accept\": \"application/json\"}) as client:\n        report(f\"MinerU cloud: requesting upload slot for {pdf_path.name}\")\n        batch_id, upload_url = _request_upload(client, pdf_path, config, key_pool)\n        size_mb = pdf_path.stat().st_size / (1024 * 1024)\n        report(f\"MinerU cloud: uploading {pdf_path.name} ({size_mb:.1f} MB)\")\n        _upload_file(pdf_path, upload_url)","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/parsing/engines/mineru/cloud.py#L59-L95","documentation":"parse_cloud requires an existing file; the given path does not resolve to a regular file.","triggerScenarios":"Passing a wrong/relative path, a file deleted between scheduling and parsing, or a directory instead of a PDF.","commonSituations":"Temp-file cleanup races, path built from untrusted input, relative path resolved against a different CWD.","solutions":["Check pdf_path.is_file() before calling and log the absolute path.","Re-check for races where uploads are queued and files later removed.","Pass absolute paths (Path(...).resolve())."],"exampleFix":"# before\nparse_cloud(\"docs/paper.pdf\", ...)\n\n# after\np = Path(\"docs/paper.pdf\").resolve()\nassert p.is_file(), p\nparse_cloud(p, ...)","handlingStrategy":"validation","validationCode":"from pathlib import Path\np = Path(pdf_path).resolve()\nassert p.is_file() and p.suffix.lower() == \".pdf\", f\"missing or non-PDF: {p}\"","typeGuard":null,"tryCatchPattern":"try:\n    parse_cloud(p, ...)\nexcept MinerUError as e:\n    if e.args[0].startswith(\"PDF file not found\"):\n        relocate_or_reupload(p)","preventionTips":["Always pass absolute resolved paths.","Keep source files alive until queued cloud jobs complete."],"tags":["mineru","cloud","file-not-found"],"backgroundTag":"file-not-found","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}