{"record":{"id":"92806c486c011ce5","repo":"opendatalab/MinerU","slug":"unsupported-protocol-version-protocol-version-e","errorCode":null,"errorMessage":"Unsupported protocol_version={protocol_version}, expected {API_PROTOCOL_VERSION}","messagePattern":"Unsupported protocol_version=(.+?), expected (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mineru/cli/router.py","lineNumber":649,"sourceCode":"                server.local_server.stop()\n\n    async def _monitor_loop(self) -> None:\n        while True:\n            await asyncio.sleep(self.settings.worker_refresh_interval_seconds)\n            await self.refresh_all()\n\n    async def refresh_all(self) -> None:\n        for server in self.servers:\n            await self._refresh_server(server)\n\n    def _update_server_from_health_payload(\n        self,\n        server: WorkerState,\n        payload: dict[str, Any],\n    ) -> None:\n        protocol_version = payload.get(\"protocol_version\")\n        if protocol_version != API_PROTOCOL_VERSION:\n            raise ValueError(\n                f\"Unsupported protocol_version={protocol_version}, expected {API_PROTOCOL_VERSION}\"\n            )\n\n        server.queued_tasks = int(payload.get(\"queued_tasks\", 0))\n        server.processing_tasks = int(payload.get(\"processing_tasks\", 0))\n        server.completed_tasks = int(payload.get(\"completed_tasks\", 0))\n        server.failed_tasks = int(payload.get(\"failed_tasks\", 0))\n        server.max_concurrent_requests = int(payload.get(\"max_concurrent_requests\", 0))\n        if server.max_concurrent_requests <= 0:\n            raise ValueError(\"max_concurrent_requests must be a positive integer\")\n        server.processing_window_size = max(\n            MIN_HEALTHY_PROCESSING_WINDOW_SIZE,\n            int(\n                payload.get(\n                    \"processing_window_size\",\n                    MIN_HEALTHY_PROCESSING_WINDOW_SIZE,\n                )\n            ),","sourceCodeStart":631,"sourceCodeEnd":667,"githubUrl":"https://github.com/opendatalab/MinerU/blob/4fe4bde114a23ee5dd637eae99b767f4669bf58c/mineru/cli/router.py#L631-L667","documentation":"ValueError raised in router.py by _update_server_from_health_payload: the health payload's protocol_version field does not equal the router's compiled-in API_PROTOCOL_VERSION. The router and workers speak a versioned protocol (task submission, health stats, result shapes); a mismatch means the installations are of different mineru versions and their message formats cannot be trusted to agree, so the server is marked unhealthy instead of being used.","triggerScenarios":"Router process from mineru 1.x managing workers from mineru 1.y (or a mixed Docker image set); one side upgraded via pip while the other was left behind; a hand-written third-party 'worker' that omits or fakes protocol_version; stale worker containers after a partial rollout.","commonSituations":"Piecemeal upgrades across a fleet; dev machines sharing a worker pool; docker-compose setups where only one service was rebuilt.","solutions":["Print/compare versions: check pip show mineru (or the version endpoint) on router and every worker.","Upgrade (or pin) all components to the exact same mineru release, then restart workers.","Rebuild all Docker images together in mixed deployments.","If writing a custom worker, echo back the router's expected API_PROTOCOL_VERSION and implement the matching payload schema."],"exampleFix":"# before\n# router: mineru 2.5.1, worker container: mineru 2.3.0 -> ValueError on health refresh\n\n# after\npip install -U 'mineru==2.5.1'   # on router host\ndocker build --pull --tag mineru-worker:2.5.1 .   # rebuild workers to same version","handlingStrategy":"validation","validationCode":"import httpx\nfrom mineru.cli.router import API_PROTOCOL_VERSION\n\ndef worker_matches_protocol(base_url: str) -> bool:\n    payload = httpx.get(f'{base_url}/health', timeout=5).json()\n    return payload.get('protocol_version') == API_PROTOCOL_VERSION","typeGuard":"def health_payload_matches(payload: dict, expected: str) -> bool:\n    return isinstance(payload, dict) and payload.get('protocol_version') == expected","tryCatchPattern":"try:\n    await router._refresh_server(server)\nexcept ValueError as exc:\n    if 'protocol_version' in str(exc):\n        logger.error('version skew on %s: pin all mineru components to one release', server.base_url)\n    raise","preventionTips":["Install the identical mineru version on router and every worker; record it in deployment manifests.","Rebuild all docker images together during upgrades — never partial rollouts of the worker pool.","Add a startup check comparing protocol_version across the pool before accepting traffic."],"tags":["mineru","version-mismatch","protocol","worker-communication","upgrade"],"backgroundTag":null,"analyzedSha":"4fe4bde114a23ee5dd637eae99b767f4669bf58c","analyzedAt":"2026-08-14T21:29:18.456Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}