{"record":{"id":"37eda44fb5f35dac","repo":"opendatalab/MinerU","slug":"payload-name-is-not-valid-json","errorCode":null,"errorMessage":"{payload_name} is not valid JSON","messagePattern":"(.+?) is not valid JSON","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mineru/cli/router.py","lineNumber":109,"sourceCode":"\ndef get_int_env(name: str, default: int, minimum: int = 0) -> int:\n    try:\n        value = int(os.getenv(name, str(default)))\n    except ValueError:\n        return default\n    if value < minimum:\n        return default\n    return value\n\n\ndef _parse_json_object_response(\n    response: httpx.Response,\n    payload_name: str,\n) -> dict[str, Any]:\n    try:\n        payload = response.json()\n    except ValueError as exc:\n        raise ValueError(f\"{payload_name} is not valid JSON\") from exc\n    if not isinstance(payload, dict):\n        raise ValueError(f\"{payload_name} must be a JSON object\")\n    return payload\n\n\ndef get_task_retention_seconds() -> int:\n    return get_int_env(\n        \"MINERU_API_TASK_RETENTION_SECONDS\",\n        DEFAULT_TASK_RETENTION_SECONDS,\n        minimum=0,\n    )\n\n\ndef get_task_cleanup_interval_seconds() -> int:\n    return get_int_env(\n        \"MINERU_API_TASK_CLEANUP_INTERVAL_SECONDS\",\n        DEFAULT_TASK_CLEANUP_INTERVAL_SECONDS,\n        minimum=1,","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/opendatalab/MinerU/blob/4fe4bde114a23ee5dd637eae99b767f4669bf58c/mineru/cli/router.py#L91-L127","documentation":"ValueError('{payload_name} is not valid JSON') raised by _parse_json_object_response in mineru/cli/router.py when calling response.json() on an httpx response from a (remote or local) mineru worker fails. The HTTP body was not parseable JSON — typically an HTML error page from a proxy, an empty body, a traceback in plain text, or a gateway timeout page. It means the router could not even begin to interpret the worker's reply.","triggerScenarios":"A reverse proxy (nginx/traefik) in front of the worker returning a 502/504 HTML page; the worker crashing mid-response so the body truncates; hitting a port served by a non-mineru service; auth middleware intercepting with an HTML login page; response gzipped/garbled by a misconfigured proxy.","commonSituations":"Docker/Kubernetes setups with ingress proxies between router and workers; worker OOM producing partial responses; wrong SERVER_URL/port in the worker list pointing at another HTTP service; TLS termination rewriting bodies.","solutions":["Inspect the raw body: log response.text (first ~500 chars) to see what was actually returned — the HTML/text usually names the real problem (proxy error, port conflict).","Verify the worker URL/port: curl the health endpoint directly and confirm it returns JSON.","Fix proxy behavior: raise proxy timeouts, whitelist the route, or bypass the proxy for worker traffic.","If the body is empty after a crash, investigate worker logs for OOM/CUDA failures."],"exampleFix":"# before (debugging blind)\npayload = _parse_json_object_response(response, 'health payload')  # raises bare\n\n# after (diagnose)\ntry:\n    payload = _parse_json_object_response(response, 'health payload')\nexcept ValueError:\n    print(response.status_code, response.headers.get('content-type'))\n    print(response.text[:500])  # reveals the proxy/HTML error page\n    raise","handlingStrategy":"try-catch","validationCode":"def worker_speaks_json(base_url: str) -> bool:\n    r = requests.get(f'{base_url}/health', timeout=5)\n    return 'json' in r.headers.get('content-type', '') and r.text.lstrip()[:1] in '{['","typeGuard":"import json\n\ndef looks_like_json_object(text: str) -> bool:\n    try:\n        return isinstance(json.loads(text), dict)\n    except ValueError:\n        return False","tryCatchPattern":"try:\n    payload = _parse_json_object_response(response, 'health payload')\nexcept ValueError as exc:\n    logger.error('non-JSON worker reply: status=%s ct=%s body=%r',\n                 response.status_code, response.headers.get('content-type'), response.text[:300])\n    raise","preventionTips":["Health-check worker URLs with a JSON assertion before adding them to the router pool.","Keep proxies between router and workers transparent: no HTML error pages, generous timeouts, correct Content-Type.","Log response bodies on parse failure — the HTML/text content names the offending proxy or crash."],"tags":["mineru","httpx","json","proxy","worker-communication"],"backgroundTag":null,"analyzedSha":"4fe4bde114a23ee5dd637eae99b767f4669bf58c","analyzedAt":"2026-08-14T21:29:18.456Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}