{"record":{"id":"53b4ad2ee1a565f4","repo":"HKUDS/DeepTutor","slug":"mineru-reported-done-but-returned-no-full-zip-url","errorCode":null,"errorMessage":"MinerU reported done but returned no full_zip_url.","messagePattern":"MinerU reported done but returned no full_zip_url\\.","errorType":"exception","errorClass":"MinerUError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/parsing/engines/mineru/cloud.py","lineNumber":195,"sourceCode":"        if entry is not None:\n            state = str(entry.get(\"state\") or \"\").strip().lower()\n            last_state = state or last_state\n            if on_progress is not None:\n                progress = entry.get(\"extract_progress\") or {}\n                total_pages = progress.get(\"total_pages\")\n                report = f\"MinerU cloud: {state or 'queued'}\"\n                if total_pages:\n                    report += f\" ({progress.get('extracted_pages') or 0}/{total_pages} pages)\"\n                if report != last_report:\n                    last_report = report\n                    try:\n                        on_progress(report)\n                    except Exception:\n                        on_progress = None\n            if state == _TERMINAL_OK:\n                zip_url = str(entry.get(\"full_zip_url\") or \"\").strip()\n                if not zip_url:\n                    raise MinerUError(\"MinerU reported done but returned no full_zip_url.\")\n                return zip_url\n            if state == _TERMINAL_FAIL:\n                err = str(entry.get(\"err_msg\") or \"unknown error\")\n                raise MinerUError(f\"MinerU failed to parse the document: {err}\")\n        if time.monotonic() >= deadline:\n            raise MinerUError(\n                f\"MinerU parsing timed out after {int(timeout)}s \"\n                f\"(last state: {last_state or 'unknown'}).\"\n            )\n        time.sleep(poll_interval)\n\n\ndef verify_credentials(config: MinerUConfig) -> None:\n    \"\"\"Best-effort connectivity / token check for the Settings → MinerU \"Test\"\n    button. Requests an upload slot (which does not consume parsing quota and\n    is never followed by an upload, so it simply expires) and validates the\n    business code. Raises :class:`MinerUError` with a user-facing message on\n    any failure.\"\"\"","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/parsing/engines/mineru/cloud.py#L177-L213","documentation":"The polling loop saw a terminal-success state but the entry had no full_zip_url, so there is nothing to download. Defensive check against an inconsistent API response.","triggerScenarios":"Polling /api/v4/extract results returning state=done with a missing/blank full_zip_url field.","commonSituations":"MinerU backend keeping results in cold storage briefly, partial response, or API contract change.","solutions":["Retry the parse job after a short delay — usually transient.","Log the raw entry JSON to confirm the field name/shape.","Check MinerU API docs/changelog if persistent."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    parse_cloud(...)\nexcept MinerUError as e:\n    if \"full_zip_url\" in str(e):\n        retry_with_backoff()","preventionTips":["Treat missing-fields-on-done as transient; retry once before alerting.","Log full API entries to detect contract changes."],"tags":["mineru","cloud","polling","api-contract"],"backgroundTag":"api-unexpected-response-shape","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}